Final revision: audit fixes, cross-scheme comparisons, known-plaintext stage
- 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
This commit is contained in:
+150
-11
@@ -249,33 +249,42 @@ def stage_D():
|
||||
fams = {}
|
||||
# random fixed masks
|
||||
set_seed(7); fams["random"] = random_mask(U, Lp)
|
||||
# Walsh-Hadamard rows (orthogonal)
|
||||
Hd = torch.tensor(hadamard(Lp)[:U], dtype=torch.float32) # ||row||=sqrt(Lp)
|
||||
# Walsh-Hadamard rows (orthogonal). Row 0 of the Sylvester
|
||||
# construction is the all-ones vector, which any adversary can write
|
||||
# down, so it is excluded and the users take rows 1 to U.
|
||||
Hd = torch.tensor(hadamard(Lp)[1:U + 1], dtype=torch.float32)
|
||||
fams["hadamard"] = Hd
|
||||
ones = torch.ones(U, Lp) # the cheapest possible guess
|
||||
rows = []
|
||||
for name, W in fams.items():
|
||||
m = get_model(P=P, vu=vu, d=d, U=U, iters=4000, freeze_W=W)
|
||||
lg = eval_ser_sse(m, [10.0], frames=500_000)[0]
|
||||
ew = eve_wrong_mask(U, Lp, seed=20260813).to(DEVICE)
|
||||
ev = eval_ser_eve(m, ew, [10.0], frames=500_000)[0]
|
||||
ev1 = eval_ser_eve(m, ones, [10.0], frames=500_000)[0]
|
||||
xc = mean_abs_xcorr(m.masks().detach())
|
||||
rows.append((name, lg, ev, xc))
|
||||
print(f" {name:9s} legit={lg:.2e} eve={ev:.3f} xcorr={xc:.4f}")
|
||||
rows.append((name, lg, ev, ev1, xc))
|
||||
print(f" {name:9s} legit={lg:.2e} eve={ev:.3f} ones={ev1:.3f} "
|
||||
f"xcorr={xc:.4f}")
|
||||
# learned masks (plain cross entropy)
|
||||
m = get_model(P=P, vu=vu, d=d, U=U, iters=4000)
|
||||
lg = eval_ser_sse(m, [10.0], frames=500_000)[0]
|
||||
ew = eve_wrong_mask(U, Lp, seed=20260813).to(DEVICE)
|
||||
ev = eval_ser_eve(m, ew, [10.0], frames=500_000)[0]
|
||||
ev1 = eval_ser_eve(m, ones, [10.0], frames=500_000)[0]
|
||||
xc = mean_abs_xcorr(m.masks().detach())
|
||||
rows.append(("learned", lg, ev, xc))
|
||||
print(f" {'learned':9s} legit={lg:.2e} eve={ev:.3f} xcorr={xc:.4f}")
|
||||
rows.append(("learned", lg, ev, ev1, xc))
|
||||
print(f" {'learned':9s} legit={lg:.2e} eve={ev:.3f} ones={ev1:.3f} "
|
||||
f"xcorr={xc:.4f}")
|
||||
# regularized key learning (orthogonality + constant modulus)
|
||||
mr = get_model_reg(P=P, vu=vu, d=d, U=U, iters=4000)
|
||||
lgr = eval_ser_sse(mr, [10.0], frames=500_000)[0]
|
||||
evr = eval_ser_eve(mr, ew, [10.0], frames=500_000)[0]
|
||||
evr1 = eval_ser_eve(mr, ones, [10.0], frames=500_000)[0]
|
||||
xcr = mean_abs_xcorr(mr.masks().detach())
|
||||
rows.append(("learned_reg", lgr, evr, xcr))
|
||||
print(f" {'learn_reg':9s} legit={lgr:.2e} eve={evr:.3f} xcorr={xcr:.4f}")
|
||||
rows.append(("learned_reg", lgr, evr, evr1, xcr))
|
||||
print(f" {'learn_reg':9s} legit={lgr:.2e} eve={evr:.3f} ones={evr1:.3f} "
|
||||
f"xcorr={xcr:.4f}")
|
||||
# jamming robustness of plain vs regularized keys (blind jammer)
|
||||
jsr = [-10.0, -5.0, 0.0, 5.0, 10.0, 15.0, 20.0]
|
||||
jb_plain = eval_ser_jam(m, 10.0, jsr, frames=300_000, mode="blind")
|
||||
@@ -284,7 +293,8 @@ def stage_D():
|
||||
["jsr_db", "plain", "regularized"],
|
||||
[(j, jb_plain[i], jb_reg[i]) for i, j in enumerate(jsr)])
|
||||
write_csv(DATA / "sec_maskfam.csv",
|
||||
["family", "legit_ser", "eve_ser", "mask_xcorr"], rows)
|
||||
["family", "legit_ser", "eve_ser", "eve_ones_ser",
|
||||
"mask_xcorr"], rows)
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
@@ -407,9 +417,11 @@ def stage_E():
|
||||
|
||||
@torch.no_grad()
|
||||
def eval_scheme_permuted_eve(model: SSE, snr_db, frames, perms,
|
||||
chunk=100_000, seed=777):
|
||||
chunk=100_000, seed=777, eve_perms=None):
|
||||
"""Eve for S3: sees the per-user permuted tx, holds the PUBLIC masks
|
||||
but not the permutation, decodes user 0 raw."""
|
||||
but not the permutation, decodes user 0 raw. When eve_perms is given,
|
||||
Eve first undoes the permutation she believes was used, which models
|
||||
an attacker holding a partially recovered permutation key."""
|
||||
model.eval().to(DEVICE)
|
||||
Bn = model.unit_codebook()
|
||||
true_m = model.masks()
|
||||
@@ -431,6 +443,9 @@ def eval_scheme_permuted_eve(model: SSE, snr_db, frames, perms,
|
||||
y_rx = h[:, None, None] * y + sigma * torch.randn(
|
||||
n, model.P, model.L, device=DEVICE)
|
||||
r = y_rx / h[:, None, None].clamp_min(1e-6)
|
||||
if eve_perms is not None:
|
||||
inv = torch.argsort(eve_perms[0]).to(r.device)
|
||||
r = r.reshape(n, d)[:, inv].reshape(n, model.P, model.L)
|
||||
cand = Bn * true_m[0][None, :]
|
||||
scores = torch.einsum("npl,vl->npv", r, cand)
|
||||
wrong = (scores.argmax(-1) != digits[:, 0]).any(dim=1)
|
||||
@@ -508,6 +523,128 @@ def stage_F():
|
||||
["L", "K", "best_rho", "eve_ser"], rows)
|
||||
|
||||
|
||||
def partial_perm(true_perm: torch.Tensor, frac: float, gen: torch.Generator):
|
||||
"""A permutation that agrees with true_perm on a fraction frac of the
|
||||
positions and is scrambled on the rest, which is what an attacker
|
||||
holding part of a permutation key would have."""
|
||||
d = true_perm.numel()
|
||||
k = int(round(frac * d))
|
||||
idx = torch.randperm(d, generator=gen)
|
||||
keep, rest = idx[:k], idx[k:]
|
||||
out = true_perm.clone()
|
||||
if rest.numel() > 1:
|
||||
out[rest] = true_perm[rest][torch.randperm(rest.numel(), generator=gen)]
|
||||
return out
|
||||
|
||||
|
||||
TRIALS_PERM = 60
|
||||
|
||||
|
||||
def stage_I():
|
||||
"""Key sensitivity of three schemes on one axis.
|
||||
|
||||
The axis is the fraction of the key the attacker has recovered. For
|
||||
the proposed scheme that fraction is the normalized correlation
|
||||
between the guessed and the true mask. For the permutation scheme it
|
||||
is the fraction of positions the guessed permutation places
|
||||
correctly. For the index cipher it is the fraction of pad bits the
|
||||
attacker knows, whose error rate is the closed form
|
||||
1 - 2^{-(1-f) log2 V} because the unknown bits are uniform.
|
||||
"""
|
||||
print("[I] key sensitivity across schemes ...")
|
||||
m = get_model(iters=4000)
|
||||
F = 600_000 # more frames per point for a smooth curve
|
||||
TRIALS_MASK = 12 # independent substitute keys per point
|
||||
d = m.P * m.L
|
||||
true_m = m.masks().detach().cpu()
|
||||
gen = torch.Generator().manual_seed(31)
|
||||
gp = torch.Generator().manual_seed(11)
|
||||
gperm = torch.randperm(d, generator=gp)
|
||||
perms = gperm[None].repeat(m.users, 1)
|
||||
# a marker grid comparable to the other result figures, with the
|
||||
# spacing tightened only where the curves fall
|
||||
fracs = [0.0, 0.2, 0.4, 0.6, 0.75, 0.85, 0.9, 0.94, 0.97, 1.0]
|
||||
bits = math.log2(m.V)
|
||||
rows = []
|
||||
for f in fracs:
|
||||
acc_m = []
|
||||
for t in range(TRIALS_MASK):
|
||||
mt = correlated_masks(true_m, f, gen)
|
||||
acc_m.append(eval_ser_eve(m, mt, [10.0],
|
||||
frames=F // TRIALS_MASK,
|
||||
seed=777 + 17 * t)[0])
|
||||
ser_mask = sum(acc_m) / len(acc_m)
|
||||
# a partial permutation is combinatorially lumpy, so the point
|
||||
# is averaged over independent draws of which positions the
|
||||
# attacker holds
|
||||
acc = []
|
||||
for t in range(TRIALS_PERM):
|
||||
pp = partial_perm(gperm, f, gen)
|
||||
pperms = pp[None].repeat(m.users, 1)
|
||||
acc.append(eval_scheme_permuted_eve(m, 10.0, F // TRIALS_PERM,
|
||||
perms, eve_perms=pperms,
|
||||
seed=777 + 13 * t))
|
||||
ser_perm = sum(acc) / len(acc)
|
||||
ser_pad = 1.0 - 2.0 ** (-(1.0 - f) * bits)
|
||||
rows.append((f, ser_mask, ser_perm, ser_pad))
|
||||
print(f" f={f:.3f} mask={ser_mask:.4f} perm={ser_perm:.4f} "
|
||||
f"pad={ser_pad:.4f}")
|
||||
write_csv(DATA / "sec_sens_cmp.csv",
|
||||
["frac", "ser_mask", "ser_perm", "ser_pad"], rows)
|
||||
|
||||
|
||||
def stage_J():
|
||||
"""Brute-force search against three schemes at the same key length.
|
||||
|
||||
Keyed masking: K random unit keys, keep the best correlation, map it
|
||||
through the measured sensitivity curve of stage I.
|
||||
Permutation key: K random permutations of the d positions, keep the
|
||||
one that places the most positions correctly, map the resulting
|
||||
fraction through the same sensitivity curve.
|
||||
Index cipher: K random pads out of the 2^{log2 V} possible pads, so
|
||||
the attacker succeeds with probability K/V on each symbol.
|
||||
"""
|
||||
print("[J] brute-force search across schemes ...")
|
||||
import numpy as np
|
||||
cmp_rows = list(csv_rows(DATA / "sec_sens_cmp.csv"))
|
||||
f_arr = np.array([float(r["frac"]) for r in cmp_rows])
|
||||
mask_arr = np.array([float(r["ser_mask"]) for r in cmp_rows])
|
||||
perm_arr = np.array([float(r["ser_perm"]) for r in cmp_rows])
|
||||
|
||||
d, L, V = 64, 16, 65536
|
||||
ks = [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000]
|
||||
rng = np.random.default_rng(2026)
|
||||
trials = 400
|
||||
rows = []
|
||||
for K in ks:
|
||||
# keyed masking: best |first coordinate| of K random unit vectors
|
||||
best_kappa = np.empty(trials)
|
||||
best_frac = np.empty(trials)
|
||||
for t in range(trials):
|
||||
g = rng.standard_normal((K, L))
|
||||
g /= np.linalg.norm(g, axis=1, keepdims=True)
|
||||
best_kappa[t] = np.abs(g[:, 0]).max()
|
||||
# permutation: fraction of fixed points, Binomial(d, 1/d) per
|
||||
# draw, so the best of K draws is the max of K such counts
|
||||
best_frac[t] = rng.binomial(d, 1.0 / d, size=K).max() / d
|
||||
ser_mask = float(np.mean(np.interp(best_kappa, f_arr, mask_arr)))
|
||||
ser_perm = float(np.mean(np.interp(best_frac, f_arr, perm_arr)))
|
||||
ser_pad = 1.0 - min(1.0, K / V)
|
||||
rows.append((K, ser_mask, ser_perm, ser_pad,
|
||||
float(best_kappa.mean()), float(best_frac.mean())))
|
||||
print(f" K={K:8d} mask={ser_mask:.4f} perm={ser_perm:.4f} "
|
||||
f"pad={ser_pad:.4f}")
|
||||
write_csv(DATA / "sec_brute_cmp.csv",
|
||||
["K", "ser_mask", "ser_perm", "ser_pad",
|
||||
"best_kappa", "best_frac"], rows)
|
||||
|
||||
|
||||
def csv_rows(path):
|
||||
import csv as _csv
|
||||
with open(path) as f:
|
||||
yield from _csv.DictReader(f)
|
||||
|
||||
|
||||
def main():
|
||||
print(f"device={DEVICE}")
|
||||
stage_A()
|
||||
@@ -516,6 +653,8 @@ def main():
|
||||
stage_D()
|
||||
stage_E()
|
||||
stage_F()
|
||||
stage_I()
|
||||
stage_J()
|
||||
print("[done] full-scale security CSVs in", DATA)
|
||||
|
||||
|
||||
|
||||
+40
-16
@@ -1,9 +1,11 @@
|
||||
"""Generate the LaTeX rows of the two result tables from the CSVs, so
|
||||
"""Generate the LaTeX rows of the three result tables from the CSVs, so
|
||||
that every table in the paper is reproducible from data/ (TIFS mandate).
|
||||
Prints the tabular body; paste into main.tex without edits.
|
||||
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"
|
||||
@@ -17,39 +19,61 @@ NAME = {
|
||||
"random": "Random",
|
||||
"hadamard": "Walsh-Hadamard",
|
||||
"learned": "Learned",
|
||||
"learned_reg": r"Regularized~\eqref{eq:regloss}",
|
||||
}
|
||||
RECEIVER = {
|
||||
"legit": "Legitimate", "oma": "OMA",
|
||||
"insider": "Insider", "eve": "Outsider eavesdropper",
|
||||
}
|
||||
|
||||
|
||||
def f3(x: str) -> str:
|
||||
"""Three decimals, or an em-dash for a value that does not apply."""
|
||||
try:
|
||||
return f"{float(x):.3f}"
|
||||
except ValueError:
|
||||
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 = sorted(rows, key=lambda r: order.index(r["scheme"]))
|
||||
rows.sort(key=lambda r: order.index(r["scheme"]))
|
||||
for r in rows:
|
||||
cells = [f3(r["legit_ser"]), f3(r["eve_out"]), f3(r["eve_in"]),
|
||||
f3(r["jam0_ser"])]
|
||||
if r["scheme"] == "proposed":
|
||||
cells = [rf"$\mathbf{{{c}}}$" for c in cells]
|
||||
else:
|
||||
cells = [f"${c}$" if c != "--" else "--" for c in cells]
|
||||
b = r["scheme"] == "proposed"
|
||||
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")):
|
||||
print(f"{NAME[r['family']]} & ${f3(r['legit_ser'])}$ & "
|
||||
f"${f3(r['eve_ser'])}$ & ${f3(r['mask_xcorr'])}$" + r" \\")
|
||||
cells = [cell(r[k], False) for k in
|
||||
("legit_ser", "eve_ser", "eve_ones_ser", "mask_xcorr")]
|
||||
print(f"{NAME[r['family']]} & " + " & ".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" \\")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
compare_table()
|
||||
print()
|
||||
maskfam_table()
|
||||
compare_table(); print()
|
||||
maskfam_table(); print()
|
||||
real_table()
|
||||
|
||||
+58
-17
@@ -7,7 +7,8 @@ Label dictionary is fixed here and copied verbatim into tables and prose.
|
||||
fig_sec_keylen.pdf : SER vs key length L (Fig. 3)
|
||||
fig_sec_jam.pdf : target-user SER vs JSR (Fig. 4)
|
||||
fig_sec_sens.pdf : Eve SER vs key correlation (Fig. 5)
|
||||
fig_sec_brute.pdf : Eve SER vs number of key guesses (Fig. 6)
|
||||
fig_sec_brute.pdf : Eve SER vs number of key guesses (Fig. 6)
|
||||
fig_sec_brute_rho.pdf : best key correlation vs guesses (Fig. 7)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from pathlib import Path
|
||||
@@ -159,14 +160,23 @@ def fig_jam():
|
||||
|
||||
|
||||
def fig_sens():
|
||||
r = load("sec_sens.csv")
|
||||
x = col(r, "rho")
|
||||
"""Key sensitivity of three schemes on one axis, the fraction of the
|
||||
key the attacker holds. For keyed masking that fraction is the mask
|
||||
correlation, for the permutation scheme the fraction of positions
|
||||
placed correctly, for the index cipher the fraction of pad bits
|
||||
known."""
|
||||
r = load("sec_sens_cmp.csv")
|
||||
x = col(r, "frac")
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, col(r, "eve_ser"), color=C_EVE, marker="s", ls="-",
|
||||
label=LBL["eve_key"])
|
||||
ax.plot(x, col(r, "ser_mask"), color=C_LEGIT, marker="o", ls="-",
|
||||
label="Keyed masking")
|
||||
ax.plot(x, col(r, "ser_perm"), color=C_EVE, marker="s", ls="--",
|
||||
label="Permutation key")
|
||||
ax.plot(x, col(r, "ser_pad"), color=C_PUB, marker="v", ls="-.",
|
||||
label="Index cipher")
|
||||
chance = 1.0 - (1.0 / 16.0) ** 4
|
||||
ax.axhline(chance, color=C_CH, ls="-.", lw=0.9, label=LBL["chance"])
|
||||
ax.set_xlabel(r"Key correlation $\kappa$")
|
||||
ax.axhline(chance, color=C_CH, ls=":", lw=0.9, label=LBL["chance"])
|
||||
ax.set_xlabel("Fraction of the key recovered")
|
||||
ax.set_ylabel("Eavesdropper SER")
|
||||
ax.set_xlim(0, 1)
|
||||
ax.legend(loc="lower left")
|
||||
@@ -174,21 +184,45 @@ def fig_sens():
|
||||
|
||||
|
||||
def fig_brute():
|
||||
"""Brute-force search against the three keyed schemes at the same
|
||||
key length, each mapped through its own sensitivity curve."""
|
||||
r = load("sec_brute_cmp.csv")
|
||||
x = col(r, "K")
|
||||
fig, ax = plt.subplots()
|
||||
ax.semilogx(x, col(r, "ser_mask"), color=C_LEGIT, marker="o", ls="-",
|
||||
label="Keyed masking")
|
||||
ax.semilogx(x, col(r, "ser_perm"), color=C_EVE, marker="s", ls="--",
|
||||
label="Permutation key")
|
||||
ax.semilogx(x, col(r, "ser_pad"), color=C_PUB, marker="v", ls="-.",
|
||||
label="Index cipher")
|
||||
kl = load("sec_keylen.csv")
|
||||
legit = float([q for q in kl if int(q["L"]) == 16][0]["legit_ser"])
|
||||
ax.axhline(legit, color=C_OMA, ls=":", lw=0.9, label=LBL["legit"])
|
||||
ax.set_xlabel("Number of key guesses $K$")
|
||||
ax.set_ylabel("Eavesdropper SER")
|
||||
ax.set_ylim(-0.03, 1.05)
|
||||
ax.legend(loc="center left")
|
||||
save(fig, "fig_sec_brute")
|
||||
|
||||
|
||||
def fig_brute_rho():
|
||||
"""Best key correlation a search of size K reaches, per key length.
|
||||
This is a property of the key space alone."""
|
||||
r = load("sec_brute.csv")
|
||||
fig, ax = plt.subplots()
|
||||
sty = {8: ("#c0392b", "o"), 16: ("#2c5fa8", "s"),
|
||||
32: ("#16a085", "v"), 64: ("#8e44ad", "P")}
|
||||
for Lp in [8, 16, 32, 64]:
|
||||
for Lp, (c, mk) in sty.items():
|
||||
rows = [row for row in r if int(row["L"]) == Lp]
|
||||
ks = [float(row["K"]) for row in rows]
|
||||
ser = [float(row["eve_ser"]) for row in rows]
|
||||
c, mk = sty[Lp]
|
||||
ax.semilogx(ks, ser, color=c, marker=mk, ls="-",
|
||||
label=f"$L={Lp}$")
|
||||
ax.semilogx([float(x["K"]) for x in rows],
|
||||
[float(x["best_rho"]) for x in rows],
|
||||
color=c, marker=mk, ls="-", label=f"$L={Lp}$")
|
||||
ax.axhline(0.96, color=C_CH, ls="-.", lw=0.9, label="Break threshold")
|
||||
ax.set_xlabel("Number of key guesses $K$")
|
||||
ax.set_ylabel("Eavesdropper SER")
|
||||
ax.legend(loc="lower left")
|
||||
save(fig, "fig_sec_brute")
|
||||
ax.set_ylabel(r"Best key correlation $\kappa$")
|
||||
ax.set_ylim(0, 1.05)
|
||||
ax.legend(loc="upper left")
|
||||
save(fig, "fig_sec_brute_rho")
|
||||
|
||||
|
||||
def fig_real():
|
||||
@@ -221,7 +255,13 @@ def fig_kpa():
|
||||
ser = [float(row["eve_ser"]) for row in rows]
|
||||
ax.semilogx(n, ser, color=c, marker=mk, ls="-",
|
||||
label=f"{int(snr)} dB")
|
||||
ax.axhline(0.304, color=C_OMA, ls=":", lw=0.9, label=LBL["legit"])
|
||||
# legitimate reference measured with the SAME estimator as the
|
||||
# eavesdropper curves, namely the four-user average of eval_ser_sse
|
||||
# at L=16, taken from sec_keylen.csv rather than from the user-1
|
||||
# convention of the scheme-comparison table
|
||||
kl = load("sec_keylen.csv")
|
||||
legit = float([r for r in kl if int(r["L"]) == 16][0]["legit_ser"])
|
||||
ax.axhline(legit, color=C_OMA, ls=":", lw=0.9, label=LBL["legit"])
|
||||
ax.set_xlabel("Known-plaintext frames $N$")
|
||||
ax.set_ylabel("Eavesdropper SER")
|
||||
ax.set_xscale("log", base=2)
|
||||
@@ -236,6 +276,7 @@ def main():
|
||||
try:
|
||||
fig_sens()
|
||||
fig_brute()
|
||||
fig_brute_rho()
|
||||
except FileNotFoundError:
|
||||
print("[skip] attack-difficulty CSVs not present yet")
|
||||
try:
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
K,ser_mask,ser_perm,ser_pad,best_kappa,best_frac
|
||||
1,0.9993678262,0.9999544349,0.9999847412,0.1909643153,0.0160546875
|
||||
10,0.9948030015,0.9999095052,0.9998474121,0.4626973216,0.041015625
|
||||
100,0.9825717055,0.9998648567,0.9984741211,0.6342127242,0.0658203125
|
||||
1000,0.9568330187,0.9998315989,0.9847412109,0.7432459045,0.084296875
|
||||
10000,0.909109588,0.9997988333,0.8474121094,0.8165387856,0.1025
|
||||
100000,0.8435049061,0.9997690911,0,0.8680494354,0.1190234375
|
||||
1000000,0.7503523409,0.9997409661,0,0.905556646,0.1346484375
|
||||
|
@@ -1,5 +1,5 @@
|
||||
family,legit_ser,eve_ser,mask_xcorr
|
||||
random,0.64962,0.998488,0.2709003091
|
||||
hadamard,0.2568,0.9995605,0
|
||||
learned,0.2762895,0.9999285,0.007116591092
|
||||
learned_reg,0.315952,0.9999555,0.01121100038
|
||||
family,legit_ser,eve_ser,eve_ones_ser,mask_xcorr
|
||||
random,0.64962,0.998488,0.999988,0.2709003091
|
||||
hadamard,0.257299,0.9999905,0.9999755,0
|
||||
learned,0.2762895,0.9999285,0.99999,0.007116591092
|
||||
learned_reg,0.315952,0.9999555,0.9999175,0.01121100038
|
||||
|
||||
|
+7
-7
@@ -1,8 +1,8 @@
|
||||
jsr_db,plain,regularized
|
||||
-10,0.46879,0.4691633333
|
||||
-5,0.6335366667,0.6307333333
|
||||
0,0.8056466667,0.8018166667
|
||||
5,0.9187366667,0.91602
|
||||
10,0.9708533333,0.9693866667
|
||||
15,0.9902133333,0.9896433333
|
||||
20,0.9967333333,0.9965933333
|
||||
-10,0.46703,0.4719866667
|
||||
-5,0.6348,0.6301533333
|
||||
0,0.8073133333,0.80122
|
||||
5,0.9191366667,0.91577
|
||||
10,0.9707266667,0.96911
|
||||
15,0.9901433333,0.99006
|
||||
20,0.9968333333,0.9964333333
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
frac,ser_mask,ser_perm,ser_pad
|
||||
0,0.99998375,0.9999833333,0.9999847412
|
||||
0.2,0.9997954167,0.9996233333,0.999859778
|
||||
0.4,0.9985329167,0.9949383333,0.9987114181
|
||||
0.6,0.99153625,0.9540933333,0.9881584643
|
||||
0.75,0.9644704167,0.880335,0.9375
|
||||
0.85,0.88742875,0.7279433333,0.8105354292
|
||||
0.9,0.7780379167,0.5716733333,0.6701230223
|
||||
0.94,0.6256225,0.4316466667,0.4859430867
|
||||
0.97,0.43810125,0.3708666667,0.283022376
|
||||
1,0.2756983333,0.3021566667,0
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user