Learned-key stages, per-scheme plot styles, KM naming
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.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Learned-key counterparts of the structured-key result stages.
|
||||
|
||||
Keyed masking is realized two ways, with structured Walsh-Hadamard keys
|
||||
and with keys learned in R^L. The two differ in key space, so the paper
|
||||
reports both wherever a figure or table carries a keyed-masking result.
|
||||
This script produces the learned side of the key-length sweep, the
|
||||
jamming sweep, the known-plaintext attack, the scheme comparison and
|
||||
the refresh, writing files named *_learned.csv next to the structured
|
||||
ones.
|
||||
|
||||
Every evaluation mirrors its structured counterpart exactly: same SNR,
|
||||
same frame counts, same seeds, same evaluators. Only the key family
|
||||
differs.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
import torch
|
||||
|
||||
import exp_kpa
|
||||
from exp_full import (MAIN_D, eval_ser_eve, eval_ser_jam, eve_wrong_mask,
|
||||
get_model, mean_abs_xcorr, oma_ser_keylen)
|
||||
from sse_lib import DATA, DEVICE, eval_ser_sse, write_csv
|
||||
|
||||
SEED = 1
|
||||
|
||||
|
||||
def learned_model(d=MAIN_D, P=4, vu=16, U=4, iters=4000, seed=SEED):
|
||||
"""The learned counterpart of main_model: same everything, keys free."""
|
||||
return get_model(P=P, vu=vu, d=d, U=U, iters=iters, seed=seed)
|
||||
|
||||
|
||||
def keylen():
|
||||
"""Fig. 3's learned curve."""
|
||||
print("[learned] key length ...")
|
||||
rows = []
|
||||
for d in [32, 48, 64, 80, 96, 128, 192, 256]:
|
||||
m = learned_model(d=d)
|
||||
lg = eval_ser_sse(m, [10.0], frames=500_000)[0]
|
||||
ev = sum(eval_ser_eve(
|
||||
m, eve_wrong_mask(m.users, m.L,
|
||||
seed=20260813 + 101 * k).to(DEVICE),
|
||||
[10.0], frames=500_000 // 8)[0]
|
||||
for k in range(8)) / 8.0
|
||||
rows.append((m.L, d, lg, ev, mean_abs_xcorr(m.masks().detach()),
|
||||
oma_ser_keylen(m.L, 10.0)))
|
||||
print(" L=%3d legit %.4f eve %.4f" % (m.L, lg, ev))
|
||||
write_csv(DATA / "sec_keylen_learned.csv",
|
||||
["L", "d", "legit_ser", "eve_ser", "mask_xcorr", "oma"], rows)
|
||||
|
||||
|
||||
def jamming():
|
||||
"""Fig. 4's learned curves."""
|
||||
print("[learned] jamming ...")
|
||||
m = learned_model()
|
||||
jsr = [-10.0, -5.0, 0.0, 5.0, 10.0, 15.0, 20.0]
|
||||
blind = eval_ser_jam(m, 10.0, jsr, frames=500_000, mode="blind", target=0)
|
||||
matched = eval_ser_jam(m, 10.0, jsr, frames=500_000, mode="matched",
|
||||
target=0)
|
||||
nojam = eval_ser_jam(m, 10.0, [-40.0], frames=500_000, mode="blind",
|
||||
target=0)[0]
|
||||
write_csv(DATA / "sec_jam_learned.csv",
|
||||
["jsr_db", "blind", "matched", "nojam"],
|
||||
[(j, blind[i], matched[i], nojam) for i, j in enumerate(jsr)])
|
||||
print(" blind :", ["%.3f" % v for v in blind])
|
||||
|
||||
|
||||
def kpa():
|
||||
"""Fig. 7's learned curve. The attack is linear algebra on the key,
|
||||
so it applies to a real-valued key exactly as to a sign pattern."""
|
||||
print("[learned] known plaintext ...")
|
||||
m = learned_model()
|
||||
m.eval()
|
||||
true_m = m.masks().detach()
|
||||
nmax = max(exp_kpa.NFRAMES)
|
||||
rows = []
|
||||
for snr in exp_kpa.SNRS:
|
||||
acc = {n: [[], []] for n in exp_kpa.NFRAMES}
|
||||
for t in range(exp_kpa.TRIALS):
|
||||
gen = torch.Generator(device="cpu").manual_seed(
|
||||
exp_kpa.SEED + int(snr) + 1000 * t)
|
||||
digits, obs, h = exp_kpa.collect_known_plaintext(m, nmax, snr, gen)
|
||||
eval_seed = 777 + 31 * t + int(snr)
|
||||
for n in exp_kpa.NFRAMES:
|
||||
est = exp_kpa.solve_keys(m, digits[:n], obs[:n], h[:n])
|
||||
acc[n][0].append(exp_kpa.key_correlation(est, true_m))
|
||||
acc[n][1].append(eval_ser_eve(m, est.cpu(), [10.0],
|
||||
frames=exp_kpa.EVAL_FRAMES,
|
||||
seed=eval_seed)[0])
|
||||
for n in exp_kpa.NFRAMES:
|
||||
ks, ss = acc[n]
|
||||
rows.append((snr, n, sum(ks) / len(ks), sum(ss) / len(ss)))
|
||||
print(" %4.0f dB done" % snr)
|
||||
write_csv(DATA / "kpa_learned.csv",
|
||||
["snr_db", "n_frames", "kappa", "eve_ser"], rows)
|
||||
|
||||
|
||||
def refresh():
|
||||
"""Table VI's learned rows: the invariance refresh acts through
|
||||
eps^2 = 1 and a relabeling, so it is available to any real key."""
|
||||
print("[learned] refresh ...")
|
||||
m = learned_model()
|
||||
W0, B0 = m.W.detach().clone(), m.B.detach().clone()
|
||||
base = eval_ser_sse(m, [10.0], frames=300_000)[0]
|
||||
out = []
|
||||
for b in range(8):
|
||||
g = torch.Generator(device=DEVICE).manual_seed(5150 + b)
|
||||
xi = torch.randperm(m.L, generator=g, device=DEVICE)
|
||||
eps = torch.randint(2, (m.L,), generator=g, device=DEVICE) * 2.0 - 1.0
|
||||
tau = torch.randperm(m.users, generator=g, device=DEVICE)
|
||||
with torch.no_grad():
|
||||
m.W.copy_((W0[tau] * eps[None, :])[:, xi])
|
||||
m.B.copy_(B0[:, xi])
|
||||
lg = eval_ser_sse(m, [10.0], frames=300_000)[0]
|
||||
ev = eval_ser_eve(m, eve_wrong_mask(m.users, m.L,
|
||||
seed=20260813).to(DEVICE),
|
||||
[10.0], frames=300_000)[0]
|
||||
out.append((b, lg, ev))
|
||||
with torch.no_grad():
|
||||
m.W.copy_(W0); m.B.copy_(B0)
|
||||
write_csv(DATA / "refresh_learned.csv",
|
||||
["block", "legit_ser", "eve_ser"], out)
|
||||
print(" unrefreshed %.5f refreshed %.5f..%.5f"
|
||||
% (base, min(r[1] for r in out), max(r[1] for r in out)))
|
||||
|
||||
|
||||
def compare():
|
||||
"""Table IV's learned row: the same four columns as the structured
|
||||
scheme, under the same jammer at a JSR of 0 dB."""
|
||||
print("[learned] scheme comparison ...")
|
||||
m = learned_model()
|
||||
F = 300_000
|
||||
legit = eval_ser_sse(m, [10.0], frames=F)[0]
|
||||
out = eval_ser_eve(m, eve_wrong_mask(m.users, m.L,
|
||||
seed=20260813).to(DEVICE),
|
||||
[10.0], frames=F)[0]
|
||||
ins = eval_ser_eve(m, m.masks().detach().roll(1, 0), [10.0], frames=F)[0]
|
||||
jam = eval_ser_jam(m, 10.0, [0.0], frames=F, mode="blind", target=0)[0]
|
||||
write_csv(DATA / "compare_learned.csv",
|
||||
["scheme", "legit_ser", "eve_out", "eve_in", "jam0_ser"],
|
||||
[("proposed_learned", legit, out, ins, jam)])
|
||||
print(" legit %.4f out %.4f in %.4f jam %.4f"
|
||||
% (legit, out, ins, jam))
|
||||
|
||||
|
||||
def main():
|
||||
keylen()
|
||||
jamming()
|
||||
kpa()
|
||||
refresh()
|
||||
compare()
|
||||
print("[done] learned-key CSVs in", DATA)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+13
-10
@@ -11,15 +11,17 @@ from pathlib import Path
|
||||
DATA = Path(__file__).resolve().parents[1] / "data"
|
||||
|
||||
NAME = {
|
||||
"proposed": r"\textbf{Proposed keyed masking}",
|
||||
"proposed": r"\textbf{KM (structured)}",
|
||||
"proposed_learned": r"\textbf{KM (learned)}",
|
||||
"public_mask": "Public masks",
|
||||
"perm_key": r"Permutation key~\cite{chen2025shufflingtifs}",
|
||||
"index_cipher": "Index cipher",
|
||||
"oma_plain": "OMA (no encryption)",
|
||||
"random": "Random",
|
||||
"hadamard": "Walsh-Hadamard",
|
||||
"learned": "Learned",
|
||||
"learned_reg": r"Regularized~\eqref{eq:regloss}",
|
||||
"hadamard": "Structured",
|
||||
"learned": "Learned, plain",
|
||||
"learned_reg": r"Learned, regularized~\eqref{eq:regloss}",
|
||||
"invariant_learned": r"\textbf{Invariant, learned keys}",
|
||||
}
|
||||
RECEIVER = {
|
||||
"legit": "Legitimate", "oma": "OMA",
|
||||
@@ -57,7 +59,8 @@ def cell(x: str, bold: bool, wide: bool = False) -> str:
|
||||
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"]
|
||||
order = ["public_mask", "perm_key", "index_cipher", "oma_plain",
|
||||
"proposed", "proposed_learned"]
|
||||
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
|
||||
@@ -67,13 +70,13 @@ def compare_table():
|
||||
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"
|
||||
b = r["scheme"].startswith("proposed")
|
||||
if r["scheme"] == "oma_plain" and f3(r["jam0_ser"]) == "--":
|
||||
r["jam0_ser"] = oma_jam
|
||||
# four decimals would still print 1.0000 here, so the column
|
||||
# stays at three and the caption names the chance level
|
||||
cells = [cell(r[k], b) for k in
|
||||
("legit_ser", "eve_out", "eve_in", "jam0_ser")]
|
||||
("eve_out", "eve_in", "jam0_ser")]
|
||||
print(f"{NAME[r['scheme']]} & " + " & ".join(cells) + r" \\")
|
||||
|
||||
|
||||
@@ -84,7 +87,7 @@ def maskfam_table():
|
||||
# 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")]
|
||||
("legit_ser", "eve_ser", "mask_xcorr")]
|
||||
name = NAME[r["family"]]
|
||||
if b:
|
||||
name = r"\textbf{" + name + "}"
|
||||
@@ -94,8 +97,8 @@ def maskfam_table():
|
||||
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"]
|
||||
b = r["scheme"].startswith("Invariant")
|
||||
name = (r"\textbf{" + r["scheme"] + "}") 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'))}$ & "
|
||||
|
||||
+75
-47
@@ -56,27 +56,46 @@ plt.rcParams.update({
|
||||
})
|
||||
AXES_RECT = dict(left=0.215, right=0.970, top=0.955, bottom=0.225)
|
||||
|
||||
C_LEGIT = "#c0392b"
|
||||
C_EVE = "#2c5fa8"
|
||||
C_OMA = "#7f8c8d"
|
||||
C_CH = "#95a5a6"
|
||||
C_MATCH = "#8e44ad"
|
||||
C_PUB = "#16a085"
|
||||
C_LEARN = "#d98c00"
|
||||
C_LEGIT = "#c0392b" # KM, structured keys
|
||||
C_LEARN = "#d98c00" # KM, learned keys
|
||||
C_OMA = "#7f8c8d" # orthogonal multiple access
|
||||
C_PUB = "#16a085" # public masks
|
||||
C_PERM = "#8e44ad" # permutation key
|
||||
C_PAD = "#a0522d" # index cipher
|
||||
C_EVE = "#2c5fa8" # an adversary of KM
|
||||
C_CH = "#95a5a6" # chance and reference levels
|
||||
C_MATCH = C_PUB # the matched jammer is what public masks admit
|
||||
|
||||
# One entry per curve the figures draw. Colour identifies the scheme and
|
||||
# line style the role: solid for a legitimate rate, dashed for an
|
||||
# adversary, dash-dot for a comparison scheme, dotted for a reference.
|
||||
# Every figure reads its curves from here, so a reader who learns a
|
||||
# curve in one figure reads the same curve in the next.
|
||||
STY = {
|
||||
"km_str": dict(color=C_LEGIT, marker="o", ls="-"),
|
||||
"km_lrn": dict(color=C_LEARN, marker="d", ls="-"),
|
||||
"oma": dict(color=C_OMA, marker="^", ls=":"),
|
||||
"pub": dict(color=C_PUB, marker="v", ls="-."),
|
||||
"perm": dict(color=C_PERM, marker="X", ls="--"),
|
||||
"pad": dict(color=C_PAD, marker="P", ls="-."),
|
||||
"eve": dict(color=C_EVE, marker="s", ls="--"),
|
||||
"insider": dict(color=C_EVE, marker="v", ls="-."),
|
||||
}
|
||||
|
||||
# fixed label dictionary: tables and prose copy these strings verbatim
|
||||
LBL = {
|
||||
"legit": "Legitimate",
|
||||
"legit_learned": "Learned keys",
|
||||
"legit": "KM (structured)",
|
||||
"legit_learned": "KM (learned)",
|
||||
"oma": "OMA",
|
||||
"eve_pub": "Eavesdropper, public masks",
|
||||
"eve_key": "Eavesdropper", # the wrong-key condition is in the caption
|
||||
"chance": "Random guess",
|
||||
"nojam": "No jammer",
|
||||
"mask": "Keyed masking",
|
||||
"mask": "KM (structured)",
|
||||
"perm": "Permutation key",
|
||||
"pad": "Index cipher",
|
||||
"insider": "Insider",
|
||||
"legit_ref": "Legitimate rate",
|
||||
"outsider": "Outsider",
|
||||
}
|
||||
# deliberate-layering style for the LOWER of two coinciding curves
|
||||
@@ -207,7 +226,7 @@ def main_legit(snr_db="10"):
|
||||
def place_legend(ax, cands=("lower left", "upper left", "center left",
|
||||
"center right", "lower center", "upper right",
|
||||
"upper center", "center", "lower right"),
|
||||
sizes=(9.2, 8.8, 8.4, 8.0, 7.6, 7.2), ncol=1):
|
||||
sizes=(9.2, 8.8, 8.4, 8.0, 7.6, 7.2, 6.8, 6.4), ncol=1):
|
||||
"""Choose the location and font size whose box the fewest curve points
|
||||
fall inside, scored on rendered geometry rather than guessed from the
|
||||
data. The size sweep is what makes a long label set placeable: a
|
||||
@@ -271,23 +290,21 @@ def fig_snr():
|
||||
# legitimate and the public-mask eavesdropper coincide by
|
||||
# construction (same physical layer, public masks decode alike), so
|
||||
# the pair is deliberately layered; OMA is separate at this frame
|
||||
ax.semilogy(x, col(r, "legit"), color=C_LEGIT, marker="o", ls="-",
|
||||
ax.semilogy(x, col(r, "legit"), **STY["km_str"],
|
||||
markevery=(0, 3), label=LBL["legit"], **UNDER)
|
||||
# the learned family is the other end of the key-space trade-off,
|
||||
# so the figure carries what it costs at every SNR
|
||||
rl = load("sec_snr_learned.csv")
|
||||
ax.semilogy(col(rl, "snr_db"), col(rl, "legit"), color=C_LEARN,
|
||||
marker="d", ls="-", markevery=(2, 3),
|
||||
label=LBL["legit_learned"])
|
||||
ax.semilogy(x, col(r, "oma"), color=C_OMA, marker="^", ls=":",
|
||||
ax.semilogy(col(rl, "snr_db"), col(rl, "legit"), **STY["km_lrn"],
|
||||
markevery=(2, 3), label=LBL["legit_learned"])
|
||||
ax.semilogy(x, col(r, "oma"), **STY["oma"],
|
||||
markevery=(1, 3), label=LBL["oma"], **OVER)
|
||||
ax.semilogy(x, col(r, "eve_public"), color=C_PUB, marker="v",
|
||||
ax.semilogy(x, col(r, "eve_public"), color=STY["pub"]["color"], marker=STY["pub"]["marker"],
|
||||
ls="none", markevery=(2, 3), markerfacecolor="none",
|
||||
label=LBL["eve_pub"])
|
||||
# this figure carries two eavesdroppers, so the bare label of the
|
||||
# key-length figure would not tell them apart
|
||||
ax.semilogy(x, col(r, "eve_wrong"), color=C_EVE, marker="s", ls="--",
|
||||
label="Eavesdropper, keyed")
|
||||
ax.semilogy(x, col(r, "eve_wrong"), **STY["eve"], label="Eavesdropper, keyed")
|
||||
# the chance level lies within 3.5e-4 of the wrong-key curve, so it is
|
||||
# drawn for reference but left out of the legend, which the caption
|
||||
# names instead; five long entries leave this figure no clear corner
|
||||
@@ -295,10 +312,9 @@ def fig_snr():
|
||||
ax.set_xlabel("SNR (dB)")
|
||||
ax.set_ylabel("SER")
|
||||
ax.set_xlim(min(x), max(x))
|
||||
# the five-entry legend needs more clear space than the four-entry
|
||||
# one did, so the axis opens a further decade below the data; the
|
||||
# lower-left is empty because every curve decays
|
||||
ax.set_ylim(bottom=2e-5)
|
||||
# most of a decade below the data leaves the lower-left genuinely
|
||||
# empty, which is what gives the legend a clear berth
|
||||
ax.set_ylim(bottom=2e-4)
|
||||
place_legend(ax)
|
||||
save(fig, "fig_sec_snr")
|
||||
|
||||
@@ -310,14 +326,16 @@ def fig_keylen():
|
||||
r = load("sec_keylen.csv")
|
||||
x = col(r, "L", int)
|
||||
fig, ax = plt.subplots()
|
||||
ax.semilogy(x, col(r, "legit_ser"), color=C_LEGIT, marker="o", ls="-",
|
||||
label=LBL["legit"])
|
||||
ax.semilogy(x, col(r, "legit_ser"), **STY["km_str"], label=LBL["legit"])
|
||||
rl = load("sec_keylen_learned.csv")
|
||||
ax.semilogy(col(rl, "L", int), col(rl, "legit_ser"), **STY["km_lrn"], label=LBL["legit_learned"])
|
||||
op = [(l, v) for l, v in zip(x, col(r, "oma")) if not math.isnan(v)]
|
||||
ax.semilogy([p[0] for p in op], [p[1] for p in op], color=C_OMA,
|
||||
marker="^", ls=":", label=LBL["oma"])
|
||||
ax.semilogy(x, col(r, "eve_ser"), color=C_EVE, marker="s", ls="--",
|
||||
label=LBL["eve_key"])
|
||||
ax.semilogy([p[0] for p in op], [p[1] for p in op], **STY["oma"], label=LBL["oma"])
|
||||
ax.semilogy(x, col(r, "eve_ser"), **STY["eve"], label=LBL["eve_key"])
|
||||
ax.set_ylim(top=22.0) # headroom above the flat eavesdropper curve
|
||||
# an error rate cannot exceed one, and the room below the data holds
|
||||
# the legend, since every curve decays to the right
|
||||
ax.set_ylim(top=1.4, bottom=1.2e-2)
|
||||
ax.set_xlabel("Key length $L$")
|
||||
ax.set_ylabel("SER")
|
||||
ax.set_xscale("log", base=2)
|
||||
@@ -335,14 +353,17 @@ def fig_jam():
|
||||
x = col(r, "jsr_db")
|
||||
me = max(1, len(x) // 8)
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, col(r, "matched"), color=C_MATCH, marker="P", ls="--",
|
||||
rj = load("sec_jam_learned.csv")
|
||||
ax.plot(col(rj, "jsr_db"), col(rj, "blind"), **STY["km_lrn"],
|
||||
markevery=(1, me), label=LBL["legit_learned"])
|
||||
ax.plot(x, col(r, "matched"), **STY["pub"],
|
||||
markevery=me, label="Public masks")
|
||||
ax.plot(x, col(r, "oma_targeted"), color=C_PUB, marker="^", ls=":",
|
||||
ax.plot(x, col(r, "oma_targeted"), **STY["oma"],
|
||||
markevery=me, label=LBL["oma"])
|
||||
# the two blind curves agree to 0.002; deliberate layering
|
||||
ax.plot(x, col(r, "blind"), color=C_LEGIT, marker="o", ls="-",
|
||||
ax.plot(x, col(r, "blind"), **STY["km_str"],
|
||||
markevery=(0, me), label=LBL["mask"], **UNDER)
|
||||
ax.plot(x, col(r, "perm_blind"), color=C_EVE, marker="s", ls="-.",
|
||||
ax.plot(x, col(r, "perm_blind"), **STY["perm"],
|
||||
markevery=(me // 2, me), label=LBL["perm"], **OVER)
|
||||
nojam = float(load("sec_jam.csv")[0]["nojam"])
|
||||
# the unjammed reference is named in the caption rather than in the
|
||||
@@ -367,11 +388,11 @@ def fig_sens():
|
||||
r = load("sec_sens_cmp.csv")
|
||||
x = col(r, "frac")
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, col(r, "ser_mask"), color=C_LEGIT, marker="o", ls="-",
|
||||
ax.plot(x, col(r, "ser_mask"), **STY["km_str"],
|
||||
markevery=(0, 3), label=LBL["mask"], **UNDER)
|
||||
ax.plot(x, col(r, "ser_perm"), color=C_EVE, marker="s", ls="--",
|
||||
ax.plot(x, col(r, "ser_perm"), **STY["perm"],
|
||||
markevery=(1, 3), label=LBL["perm"], **OVER)
|
||||
ax.plot(x, col(r, "ser_pad"), color=C_PUB, marker="v", ls="-.",
|
||||
ax.plot(x, col(r, "ser_pad"), **STY["pad"],
|
||||
markevery=(2, 3), lw=1.2, mfc="none", label=LBL["pad"])
|
||||
# the chance level comes from the stored curve, not from a second
|
||||
# copy of the configuration constants
|
||||
@@ -379,7 +400,7 @@ def fig_sens():
|
||||
ax.axhline(chance, color=C_CH, ls=":", lw=0.9, label=LBL["chance"])
|
||||
# the narration reads these curves against the legitimate rate
|
||||
ax.axhline(main_legit(), color=C_OMA, ls=(0, (4, 2)), lw=0.9,
|
||||
label=LBL["legit"])
|
||||
label=LBL["legit_ref"])
|
||||
ax.set_xlabel("Fraction of the key recovered")
|
||||
ax.set_ylabel("Eavesdropper SER")
|
||||
ax.set_xlim(0, 1)
|
||||
@@ -393,15 +414,15 @@ def fig_brute():
|
||||
r = load("sec_brute_cmp.csv")
|
||||
x = col(r, "K")
|
||||
fig, ax = plt.subplots()
|
||||
ax.semilogx(x, col(r, "ser_perm"), color=C_EVE, marker="s", ls="--",
|
||||
ax.semilogx(x, col(r, "ser_perm"), **STY["perm"],
|
||||
markevery=(0, 3), label=LBL["perm"], **UNDER)
|
||||
ax.semilogx(x, col(r, "ser_pad"), color=C_PUB, marker="v", ls="-.",
|
||||
ax.semilogx(x, col(r, "ser_pad"), **STY["pad"],
|
||||
markevery=(1, 3), label=LBL["pad"], **OVER)
|
||||
ax.semilogx(x, col(r, "ser_mask"), color=C_LEGIT, marker="o", ls="-",
|
||||
ax.semilogx(x, col(r, "ser_mask"), **STY["km_str"],
|
||||
markevery=(2, 3), label=LBL["mask"])
|
||||
legit = main_legit()
|
||||
ax.axhline(legit, color=C_OMA, ls=(0, (4, 2)), lw=0.9,
|
||||
label=LBL["legit"])
|
||||
label=LBL["legit_ref"])
|
||||
ax.set_xlabel("Number of key guesses $K$")
|
||||
ax.set_ylabel("Eavesdropper SER")
|
||||
ax.set_ylim(0.0, 1.05) # keep the reference line off the spine
|
||||
@@ -415,13 +436,13 @@ def fig_real():
|
||||
fig, ax = plt.subplots()
|
||||
# insider and outsider still nearly coincide and are layered; the
|
||||
# legitimate and OMA curves are separate at this frame
|
||||
ax.semilogy(x, col(r, "ter_legit"), color=C_LEGIT, marker="o", ls="-",
|
||||
ax.semilogy(x, col(r, "ter_legit"), **STY["km_str"],
|
||||
markevery=(0, 2), label=LBL["legit"], **UNDER)
|
||||
ax.semilogy(x, col(r, "ter_oma"), color=C_OMA, marker="^", ls=":",
|
||||
ax.semilogy(x, col(r, "ter_oma"), **STY["oma"],
|
||||
markevery=(1, 2), label=LBL["oma"], **OVER)
|
||||
ax.semilogy(x, col(r, "ter_insider"), color=C_PUB, marker="v", ls="-.",
|
||||
ax.semilogy(x, col(r, "ter_insider"), **STY["insider"],
|
||||
markevery=(0, 2), label=LBL["insider"], **UNDER)
|
||||
ax.semilogy(x, col(r, "ter_eve"), color=C_EVE, marker="s", ls="--",
|
||||
ax.semilogy(x, col(r, "ter_eve"), **STY["eve"],
|
||||
markevery=(1, 2), label=LBL["outsider"], **OVER)
|
||||
ax.set_xlabel("SNR (dB)")
|
||||
ax.set_ylabel("TER")
|
||||
@@ -444,7 +465,14 @@ def fig_kpa():
|
||||
ser = [float(row["eve_ser"]) for row in rows]
|
||||
ax.semilogx(n, ser, color=c, marker=mk, ls="-",
|
||||
markevery=(off, 4), markerfacecolor="none" if off else c,
|
||||
label=LBL["mask"] + f", {int(snr)} dB")
|
||||
label=f"KM (str.), {int(snr)} dB")
|
||||
if snr == 10.0:
|
||||
kl = [q for q in load("kpa_learned.csv")
|
||||
if float(q["snr_db"]) == snr]
|
||||
ax.semilogx([float(q["n_frames"]) for q in kl],
|
||||
[float(q["eve_ser"]) for q in kl], **STY["km_lrn"],
|
||||
markevery=(2, 4),
|
||||
label=f"KM (lrn.), {int(snr)} dB")
|
||||
try:
|
||||
p = load("pkpa.csv")
|
||||
ax.semilogx(col(p, "n_frames"), col(p, "eve_ser"), color=C_MATCH,
|
||||
@@ -458,7 +486,7 @@ def fig_kpa():
|
||||
# scheme-comparison table
|
||||
legit = main_legit()
|
||||
ax.axhline(legit, color=C_OMA, ls=(0, (4, 2)), lw=0.9,
|
||||
label=LBL["legit"])
|
||||
label=LBL["legit_ref"])
|
||||
ax.set_xlabel("Known-plaintext frames $N$")
|
||||
ax.set_ylabel("Eavesdropper SER")
|
||||
ax.set_xscale("log", base=2)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
scheme,legit_ser,eve_out,eve_in,jam0_ser
|
||||
proposed_learned,0.06358416667,0.9998083333,0.9999833333,0.4046066667
|
||||
|
@@ -0,0 +1,43 @@
|
||||
snr_db,n_frames,kappa,eve_ser
|
||||
0,1,0.2127109103,0.993336
|
||||
0,2,0.7559393242,0.666508
|
||||
0,3,0.8627683729,0.392272125
|
||||
0,4,0.9179756209,0.218212875
|
||||
0,5,0.945390512,0.14425925
|
||||
0,6,0.9590921029,0.107493
|
||||
0,8,0.9725584686,0.086112625
|
||||
0,10,0.9778045967,0.07933375
|
||||
0,12,0.9831736788,0.07420325
|
||||
0,16,0.9879393309,0.070712875
|
||||
0,24,0.9925390184,0.067800375
|
||||
0,32,0.9945808738,0.06649325
|
||||
0,48,0.9965663388,0.065265
|
||||
0,64,0.9975094497,0.065024875
|
||||
10,1,0.3165432975,0.948993875
|
||||
10,2,0.9290210679,0.19776825
|
||||
10,3,0.9781143948,0.095876875
|
||||
10,4,0.9896475986,0.070495125
|
||||
10,5,0.9934410676,0.067297125
|
||||
10,6,0.9953705788,0.06628575
|
||||
10,8,0.9971551418,0.06520175
|
||||
10,10,0.9979188025,0.064684875
|
||||
10,12,0.9982561454,0.06454375
|
||||
10,16,0.9987509355,0.06426625
|
||||
10,24,0.9992754847,0.064091125
|
||||
10,32,0.9994516179,0.06386675
|
||||
10,48,0.9996520028,0.063819875
|
||||
10,64,0.999746412,0.063847125
|
||||
20,1,0.742194891,0.513137625
|
||||
20,2,0.9947786465,0.06753025
|
||||
20,3,0.9986294076,0.06438075
|
||||
20,4,0.9992210969,0.064160375
|
||||
20,5,0.9994008377,0.064080125
|
||||
20,6,0.9995701849,0.063900375
|
||||
20,8,0.9997365534,0.063852875
|
||||
20,10,0.9998067141,0.063813
|
||||
20,12,0.9998438716,0.06370225
|
||||
20,16,0.9998808399,0.063670875
|
||||
20,24,0.9999239221,0.06380125
|
||||
20,32,0.9999452353,0.063820625
|
||||
20,48,0.9999649763,0.063811
|
||||
20,64,0.9999733046,0.06377525
|
||||
|
@@ -0,0 +1,9 @@
|
||||
block,legit_ser,eve_ser
|
||||
0,0.06381666667,0.9999375
|
||||
1,0.06395583333,0.9981941667
|
||||
2,0.06397833333,0.999985
|
||||
3,0.06386833333,0.9983758333
|
||||
4,0.06338666667,0.9999908333
|
||||
5,0.06366416667,0.9999133333
|
||||
6,0.06398416667,0.9999433333
|
||||
7,0.06390583333,0.9809091667
|
||||
|
@@ -2,3 +2,4 @@ scheme,legit,eve,entropy_bits
|
||||
None (fixed key),0.05300666667,0.9997075,23.76910417
|
||||
Fresh orthogonal keys,0.1215548611,0.9996535069,23.76910417
|
||||
Invariant,0.05304121528,0.9995528819,364.5801064
|
||||
"Invariant, learned keys",0.063800,0.997200,364.5801064
|
||||
|
||||
|
@@ -4,3 +4,4 @@ public_mask,0.0529425,0.0529425,0.0529425,0.83882
|
||||
perm_key,0.0528525,0.9999925,0.0528525,0.36425
|
||||
index_cipher,0.0529425,0.9999847412,0.9999847412,0.83882
|
||||
oma_plain,0.08056383667,0.08056383667,0.08056383667,nan
|
||||
proposed_learned,0.06358416667,0.9998083333,0.9999833333,0.4046066667
|
||||
|
||||
|
@@ -0,0 +1,8 @@
|
||||
jsr_db,blind,matched,nojam
|
||||
-10,0.117582,0.40832,0.062852
|
||||
-5,0.215158,0.657572,0.062852
|
||||
0,0.404244,0.851282,0.062852
|
||||
5,0.648786,0.94662,0.062852
|
||||
10,0.839218,0.982652,0.062852
|
||||
15,0.939304,0.994184,0.062852
|
||||
20,0.979206,0.99818,0.062852
|
||||
|
@@ -0,0 +1,9 @@
|
||||
L,d,legit_ser,eve_ser,mask_xcorr,oma
|
||||
8,32,0.9297855,0.9998735,0.007307400461,0.6849191155
|
||||
12,48,0.416604,0.9997065,0.005153660662,nan
|
||||
16,64,0.2762895,0.999912,0.007116591092,0.2747696909
|
||||
20,80,0.2076175,0.999383,0.003162040841,0.2289444229
|
||||
24,96,0.1829615,0.999894,0.002973971656,0.1961714033
|
||||
32,128,0.131901,0.999616,0.005575809628,0.1524639978
|
||||
48,192,0.090206,0.9994575,0.005743456539,0.1054308944
|
||||
64,256,0.0635265,0.999637,0.006678360514,0.08056383667
|
||||
|
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