diff --git a/code/exp_learned.py b/code/exp_learned.py new file mode 100644 index 0000000..b54942e --- /dev/null +++ b/code/exp_learned.py @@ -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() diff --git a/code/make_tables.py b/code/make_tables.py index 975bf22..956134d 100644 --- a/code/make_tables.py +++ b/code/make_tables.py @@ -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'))}$ & " diff --git a/code/replot_security.py b/code/replot_security.py index 4145cd2..f33baa3 100644 --- a/code/replot_security.py +++ b/code/replot_security.py @@ -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) diff --git a/data/compare_learned.csv b/data/compare_learned.csv new file mode 100644 index 0000000..edc5d8a --- /dev/null +++ b/data/compare_learned.csv @@ -0,0 +1,2 @@ +scheme,legit_ser,eve_out,eve_in,jam0_ser +proposed_learned,0.06358416667,0.9998083333,0.9999833333,0.4046066667 diff --git a/data/kpa_learned.csv b/data/kpa_learned.csv new file mode 100644 index 0000000..6bf8c12 --- /dev/null +++ b/data/kpa_learned.csv @@ -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 diff --git a/data/refresh_learned.csv b/data/refresh_learned.csv new file mode 100644 index 0000000..4efc8c4 --- /dev/null +++ b/data/refresh_learned.csv @@ -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 diff --git a/data/refresh_summary.csv b/data/refresh_summary.csv index 01a9e4d..72c6a13 100644 --- a/data/refresh_summary.csv +++ b/data/refresh_summary.csv @@ -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 diff --git a/data/sec_compare.csv b/data/sec_compare.csv index b2e8360..0336c0a 100644 --- a/data/sec_compare.csv +++ b/data/sec_compare.csv @@ -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 diff --git a/data/sec_jam_learned.csv b/data/sec_jam_learned.csv new file mode 100644 index 0000000..efd1cac --- /dev/null +++ b/data/sec_jam_learned.csv @@ -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 diff --git a/data/sec_keylen_learned.csv b/data/sec_keylen_learned.csv new file mode 100644 index 0000000..4c1275d --- /dev/null +++ b/data/sec_keylen_learned.csv @@ -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 diff --git a/fig/fig_sec_brute.pdf b/fig/fig_sec_brute.pdf index 7c25474..b68a61a 100644 Binary files a/fig/fig_sec_brute.pdf and b/fig/fig_sec_brute.pdf differ diff --git a/fig/fig_sec_jam.pdf b/fig/fig_sec_jam.pdf index d837d9d..64f0cd8 100644 Binary files a/fig/fig_sec_jam.pdf and b/fig/fig_sec_jam.pdf differ diff --git a/fig/fig_sec_keylen.pdf b/fig/fig_sec_keylen.pdf index e0b2789..a25e44c 100644 Binary files a/fig/fig_sec_keylen.pdf and b/fig/fig_sec_keylen.pdf differ diff --git a/fig/fig_sec_kpa.pdf b/fig/fig_sec_kpa.pdf index 8795fd8..2472d20 100644 Binary files a/fig/fig_sec_kpa.pdf and b/fig/fig_sec_kpa.pdf differ diff --git a/fig/fig_sec_real.pdf b/fig/fig_sec_real.pdf index 251dd77..5643dc2 100644 Binary files a/fig/fig_sec_real.pdf and b/fig/fig_sec_real.pdf differ diff --git a/fig/fig_sec_sens.pdf b/fig/fig_sec_sens.pdf index c0dbd32..793b7da 100644 Binary files a/fig/fig_sec_sens.pdf and b/fig/fig_sec_sens.pdf differ diff --git a/fig/fig_sec_snr.pdf b/fig/fig_sec_snr.pdf index f3ae5ee..86480a3 100644 Binary files a/fig/fig_sec_snr.pdf and b/fig/fig_sec_snr.pdf differ