Covariance attack at the main configuration, stored to data/
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.
This commit is contained in:
@@ -18,16 +18,18 @@ Procedure, using nothing the threat model keeps secret:
|
|||||||
6. report the recovered-entry fraction and the eavesdropper SER, both
|
6. report the recovered-entry fraction and the eavesdropper SER, both
|
||||||
WITHOUT ever using a known index
|
WITHOUT ever using a known index
|
||||||
|
|
||||||
Run under WSL. Prints a verdict; writes nothing to data/.
|
Run under WSL. Writes data/cov_attack.csv so the manuscript sentence
|
||||||
|
it supports is traceable to a stored artifact.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import itertools
|
import itertools
|
||||||
import math
|
import math
|
||||||
|
from pathlib import Path
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from sse_lib import rayleigh_gain, DEVICE
|
from sse_lib import rayleigh_gain, DEVICE
|
||||||
from exp_full import get_model, hadamard, eval_ser_eve
|
from exp_full import main_model, hadamard, eval_ser_eve
|
||||||
|
|
||||||
|
|
||||||
def collect_frames(m, n, snr_db, seed):
|
def collect_frames(m, n, snr_db, seed):
|
||||||
@@ -94,18 +96,25 @@ def attack(m, snr_db, n_frames, seed):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
U, L = 4, 16
|
import csv
|
||||||
K0 = torch.tensor(hadamard(L)[1:U + 1], dtype=torch.float32)
|
m = main_model()
|
||||||
m = get_model(iters=4000, freeze_W=K0)
|
|
||||||
m.eval()
|
m.eval()
|
||||||
chance = 1.0 - (1.0 / m.vu) ** m.P
|
chance = 1.0 - (1.0 / m.vu) ** m.P
|
||||||
print(f"chance SER = {chance:.5f}, legitimate reference ~0.276")
|
print(f"chance SER = {chance:.5f} at the main configuration")
|
||||||
print("ciphertext-only (NO known plaintext):")
|
print("ciphertext-only (NO known plaintext):")
|
||||||
|
rows = []
|
||||||
for snr in (10.0, 20.0):
|
for snr in (10.0, 20.0):
|
||||||
for nf in (300, 1000, 10000):
|
for nf in (300, 1000, 10000):
|
||||||
frac, ser = attack(m, snr, nf, seed=1234 + nf)
|
frac, ser = attack(m, snr, nf, seed=1234 + nf)
|
||||||
|
rows.append((snr, nf, "%.4f" % frac, "%.4f" % ser))
|
||||||
print(f" {snr:4.0f} dB N={nf:6d} "
|
print(f" {snr:4.0f} dB N={nf:6d} "
|
||||||
f"key-entry recovery={frac:.3f} eve SER={ser:.4f}")
|
f"key-entry recovery={frac:.3f} eve SER={ser:.4f}")
|
||||||
|
out = Path(__file__).resolve().parents[1] / "data" / "cov_attack.csv"
|
||||||
|
with open(out, "w", newline="") as f:
|
||||||
|
w = csv.writer(f)
|
||||||
|
w.writerow(["snr_db", "n_frames", "entry_recovery", "eve_ser"])
|
||||||
|
w.writerows(rows)
|
||||||
|
print("[csv]", out)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ FIG.mkdir(exist_ok=True)
|
|||||||
plt.rcParams.update({
|
plt.rcParams.update({
|
||||||
"font.family": "serif",
|
"font.family": "serif",
|
||||||
"font.serif": ["DejaVu Serif", "Times New Roman"],
|
"font.serif": ["DejaVu Serif", "Times New Roman"],
|
||||||
# The manuscript includes each result figure at 0.70 of a 3.455 in
|
# The manuscript includes each result figure at 0.62 of a 3.455 in
|
||||||
# column while the canvas is 3.15 in, a printed scale of 0.768. Every
|
# column while the canvas is 3.15 in, a printed scale of 0.680. Every
|
||||||
# size below is therefore pre-divided by that scale so the PRINTED
|
# size below is therefore pre-divided by that scale so the PRINTED
|
||||||
# sizes are 9 pt labels, 8 pt ticks and a 6.6 pt legend. Change the
|
# sizes are 8 pt labels, 7 pt ticks and a 5.5 pt legend. Change the
|
||||||
# include width and these must change with it.
|
# include width and these must change with it.
|
||||||
"font.size": 10.4,
|
"font.size": 10.4,
|
||||||
"axes.labelsize": 10.4,
|
"axes.labelsize": 10.4,
|
||||||
@@ -50,7 +50,7 @@ plt.rcParams.update({
|
|||||||
"grid.alpha": 0.6,
|
"grid.alpha": 0.6,
|
||||||
"lines.linewidth": 1.5,
|
"lines.linewidth": 1.5,
|
||||||
"lines.markersize": 5.2,
|
"lines.markersize": 5.2,
|
||||||
"figure.figsize": (3.15, 2.36),
|
"figure.figsize": (3.15, 2.25), # shorter canvas: same printed width and font size, less page height
|
||||||
"pdf.fonttype": 42,
|
"pdf.fonttype": 42,
|
||||||
})
|
})
|
||||||
AXES_RECT = dict(left=0.205, right=0.970, top=0.955, bottom=0.215)
|
AXES_RECT = dict(left=0.205, right=0.970, top=0.955, bottom=0.215)
|
||||||
@@ -283,7 +283,7 @@ def fig_snr():
|
|||||||
ax.set_xlim(min(x), max(x))
|
ax.set_xlim(min(x), max(x))
|
||||||
# most of a decade below the data leaves the lower-left genuinely
|
# most of a decade below the data leaves the lower-left genuinely
|
||||||
# empty, which is what gives the four-entry legend a clear berth
|
# empty, which is what gives the four-entry legend a clear berth
|
||||||
ax.set_ylim(bottom=8e-4)
|
ax.set_ylim(bottom=2e-4)
|
||||||
place_legend(ax)
|
place_legend(ax)
|
||||||
save(fig, "fig_sec_snr")
|
save(fig, "fig_sec_snr")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
snr_db,n_frames,entry_recovery,eve_ser
|
||||||
|
10.0,300,0.9023,0.2583
|
||||||
|
10.0,1000,0.8047,0.6036
|
||||||
|
10.0,10000,0.8477,0.4613
|
||||||
|
20.0,300,0.7852,0.6494
|
||||||
|
20.0,1000,0.7734,0.7093
|
||||||
|
20.0,10000,0.8164,0.4676
|
||||||
|
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