aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--epare/collect_leia.ipynb21
-rw-r--r--epare/common.py7
-rw-r--r--epare/visualize.ipynb231
3 files changed, 156 insertions, 103 deletions
diff --git a/epare/collect_leia.ipynb b/epare/collect_leia.ipynb
index 00d2efc..92d9367 100644
--- a/epare/collect_leia.ipynb
+++ b/epare/collect_leia.ipynb
@@ -70,8 +70,11 @@
" \"3bf81300008131fe454a434f5076323431b7\": \"N2N9\",\n",
" \"3b9495810146545601c4\": \"N4\",\n",
" \"3bd518ff8191fe1fc38073c821100a\": \"N6\",\n",
+ " \"3b9c9580811f039067464a01005404f272fe00c0\": \"F1\",\n",
+ " \"3b90958011fe6a\": \"F2\",\n",
+ " \"3b9f95803fc7a08031e073fa21106300000083f09000bb\": \"S1S2\"\n",
"}\n",
- "card = card_map[atr.hex()]\n",
+ "card = card_map.get(atr.hex(), None)\n",
"print(card)"
]
},
@@ -162,8 +165,20 @@
" scope.setup_channel(channel=\"B\", coupling=\"DC_50\", range=0.2, offset=-0.170, enable=True)\n",
" scope.setup_trigger(channel=\"A\", threshold=0.2, direction=\"rising\", delay=0, timeout=5000, enable=True)\n",
" scope.setup_capture(channel=\"B\", enable=True)\n",
- "\n",
- "\n",
+ "elif card == \"F1\":\n",
+ " # Javacos A22 CR\n",
+ " actual_freq, n_samples = scope.setup_frequency(frequency=50_000_000, pretrig=0, posttrig=3_000_000)\n",
+ " scope.setup_channel(channel=\"A\", coupling=\"DC\", range=1, offset=0, enable=True)\n",
+ " scope.setup_channel(channel=\"B\", coupling=\"DC_50\", range=0.2, offset=-0.170, enable=True)\n",
+ " scope.setup_trigger(channel=\"A\", threshold=0.2, direction=\"rising\", delay=0, timeout=5000, enable=True)\n",
+ " scope.setup_capture(channel=\"B\", enable=True)\n",
+ "elif card == \"F2\":\n",
+ " # Javacos JC30M48 CR\n",
+ " actual_freq, n_samples = scope.setup_frequency(frequency=50_000_000, pretrig=0, posttrig=3_000_000)\n",
+ " scope.setup_channel(channel=\"A\", coupling=\"DC\", range=1, offset=0, enable=True)\n",
+ " scope.setup_channel(channel=\"B\", coupling=\"DC_50\", range=0.2, offset=-0.170, enable=True)\n",
+ " scope.setup_trigger(channel=\"A\", threshold=0.2, direction=\"rising\", delay=0, timeout=5000, enable=True)\n",
+ " scope.setup_capture(channel=\"B\", enable=True)\n",
"print(actual_freq, n_samples)"
]
},
diff --git a/epare/common.py b/epare/common.py
index bd4e1d1..bed28dc 100644
--- a/epare/common.py
+++ b/epare/common.py
@@ -139,7 +139,10 @@ class ProbMap:
def items(self):
return self.probs.items()
- def merge(self, other: "ProbMap"):
+ def narrow(self, divisors: set[int]):
+ self.probs = {k:v for k, v in self.probs.items() if k in divisors}
+
+ def merge(self, other: "ProbMap") -> None:
if self.kind != other.kind:
raise ValueError("Merging ProbMaps of different kinds leads to unexpected results.")
new_keys = set(self.keys()).union(other.keys())
@@ -154,7 +157,7 @@ class ProbMap:
self.probs = result
self.samples += other.samples
- def enrich(self, other: "ProbMap"):
+ def enrich(self, other: "ProbMap") -> None:
if self.samples != other.samples:
raise ValueError("Enriching can only work on equal amount of samples (same run, different divisors)")
if self.kind != other.kind:
diff --git a/epare/visualize.ipynb b/epare/visualize.ipynb
index 5b93146..f423f78 100644
--- a/epare/visualize.ipynb
+++ b/epare/visualize.ipynb
@@ -25,6 +25,7 @@
"import numpy as np\n",
"\n",
"from tqdm.auto import tqdm, trange\n",
+ "from statsmodels.stats.proportion import proportion_confint\n",
"\n",
"from pyecsca.ec.mult import *\n",
"from pyecsca.misc.utils import TaskExecutor\n",
@@ -104,7 +105,10 @@
" res = {}\n",
" for mult, results in multiples.items():\n",
" res[mult] = process_small_scalars(results, divisors)\n",
- " return res"
+ " return res\n",
+ "\n",
+ "def conf_interval(p: float, samples: int, alpha: float = 0.05) -> tuple[float, float]:\n",
+ " return proportion_confint(round(p*samples), samples, alpha, method=\"wilson\")"
]
},
{
@@ -159,22 +163,12 @@
]
},
{
- "cell_type": "markdown",
- "id": "51aa1d77-2ea5-49b4-8d2b-c73b88947430",
- "metadata": {},
- "source": [
- "Even though we list `secp256r1` here, it is only an API choice, the curve is not used for anything. The number 256 might as well be in its place."
- ]
- },
- {
"cell_type": "code",
"execution_count": null,
"id": "638f8634-1f6e-4844-a796-096611dfbac2",
"metadata": {},
"outputs": [],
"source": [
- "category = \"secg\"\n",
- "curve = \"secp256r1\"\n",
"bits = 256\n",
"num_workers = 28"
]
@@ -188,6 +182,14 @@
]
},
{
+ "cell_type": "markdown",
+ "id": "8b008248-a0aa-41fa-933c-f325f8eec31b",
+ "metadata": {},
+ "source": [
+ "## Configuration"
+ ]
+ },
+ {
"cell_type": "code",
"execution_count": null,
"id": "4d2a0f19-8275-4db8-b3fc-c930d8ba2177",
@@ -197,6 +199,7 @@
"selected_mults = all_mults\n",
"divisor_name = \"all\"\n",
"kind = \"precomp+necessary\"\n",
+ "showci = False\n",
"selected_divisors = divisor_map[divisor_name]"
]
},
@@ -208,26 +211,26 @@
"outputs": [],
"source": [
"# This cell computes the probmaps from the multiplication result chunks. Only do this on small amount of chunks.\n",
- "distributions_mults = {}\n",
- "files = sorted(glob.glob(f\"multiples_{bits}_{kind}_chunk*.pickle\"))\n",
- "with TaskExecutor(max_workers=num_workers) as pool:\n",
- " for fname in files:\n",
- " pool.submit_task(fname,\n",
- " load_chunk,\n",
- " fname, selected_divisors)\n",
- " for fname, future in tqdm(pool.as_completed(), leave=False, total=len(pool.tasks), smoothing=0):\n",
- " if error := future.exception():\n",
- " print(f\"Error {fname}, {error}\")\n",
- " continue\n",
- " new_distrs = future.result()\n",
- " for mult, prob_map in new_distrs.items():\n",
- " if mult in distributions_mults:\n",
- " distributions_mults[mult].merge(prob_map)\n",
- " else:\n",
- " distributions_mults[mult] = prob_map\n",
+ "# distributions_mults = {}\n",
+ "# files = sorted(glob.glob(f\"multiples_{bits}_{kind}_chunk*.pickle\"))\n",
+ "# with TaskExecutor(max_workers=num_workers) as pool:\n",
+ "# for fname in files:\n",
+ "# pool.submit_task(fname,\n",
+ "# load_chunk,\n",
+ "# fname, selected_divisors)\n",
+ "# for fname, future in tqdm(pool.as_completed(), leave=False, total=len(pool.tasks), smoothing=0):\n",
+ "# if error := future.exception():\n",
+ "# print(f\"Error {fname}, {error}\")\n",
+ "# continue\n",
+ "# new_distrs = future.result()\n",
+ "# for mult, prob_map in new_distrs.items():\n",
+ "# if mult in distributions_mults:\n",
+ "# distributions_mults[mult].merge(prob_map)\n",
+ "# else:\n",
+ "# distributions_mults[mult] = prob_map\n",
"# Save\n",
- "with open(f\"{divisor_name}_{kind}_distrs.pickle\", \"wb\") as f:\n",
- " pickle.dump(distributions_mults, f)"
+ "# with open(f\"{divisor_name}_{kind}_distrs.pickle\", \"wb\") as f:\n",
+ "# pickle.dump(distributions_mults, f)"
]
},
{
@@ -266,6 +269,15 @@
]
},
{
+ "cell_type": "markdown",
+ "id": "9b6f169b-07b3-4b27-ba36-8b90418cd072",
+ "metadata": {},
+ "source": [
+ "## Plots (nocomb)\n",
+ "Let's visualize all the divisor groups while looking at the multipliers and countermeasures except the comb-like ones."
+ ]
+ },
+ {
"cell_type": "code",
"execution_count": null,
"id": "906b5d78-b3a4-4cbb-8051-092d411ba735",
@@ -278,6 +290,7 @@
" plot_divisors = sorted(divisor_map[divisor_name])\n",
" L = len(plot_divisors)\n",
" N = len(plot_mults)\n",
+ " x = list(range(L))\n",
" \n",
" fig = plt.figure(figsize=(L/4+10, 24))\n",
" ax = plt.subplot(111)\n",
@@ -285,21 +298,27 @@
" vals = np.zeros((N, L))\n",
" n_samples = 0\n",
" for i, mult in enumerate(plot_mults):\n",
- " y_values = [distributions_mults[mult][l] for l in plot_divisors]\n",
+ " probmap = distributions_mults[mult]\n",
+ " y_values = [probmap[l] for l in plot_divisors]\n",
" vals[i,] = y_values\n",
- " ax.plot(list(range(L)), y_values, color=colors[mult], linestyle=styles[mult], marker=markers[mult], label=str(mult))\n",
- " n_samples += distributions_mults[mult].samples\n",
+ " ax.plot(x, y_values, color=colors[mult], linestyle=styles[mult], marker=markers[mult], label=str(mult))\n",
+ " if showci:\n",
+ " cis = [conf_interval(p, probmap.samples) for p in y_values]\n",
+ " ci_low = [ci[0] for ci in cis]\n",
+ " ci_high = [ci[1] for ci in cis]\n",
+ " ax.fill_between(x, ci_low, ci_high, color=\"black\", alpha=0.1)\n",
+ " n_samples += probmap.samples\n",
" \n",
" ax.set_title(f\"{divisor_name} ({kind})\\nSamples(avg): \" + str(n_samples//N))\n",
" \n",
" var = np.var(vals, axis=0)\n",
- " ax.plot(list(range(L)), var / np.max(var), label=\"cross-mult variance (normalized)\", ls=\"--\", lw=2, color=\"black\")\n",
+ " ax.plot(x, var / np.max(var), label=\"cross-mult variance (normalized)\", ls=\"--\", lw=2, color=\"black\")\n",
" \n",
" ax.set_xlabel(\"divisors\")\n",
" ax.set_ylabel(\"error probability\")\n",
" ax.set_yticks(majticks)\n",
" ax.set_yticks(minticks, minor=True)\n",
- " ax.set_xticks(list(range(L)), plot_divisors, rotation=90)\n",
+ " ax.set_xticks(x, plot_divisors, rotation=90)\n",
" \n",
" ax.grid(axis=\"y\", which=\"major\", alpha=0.7)\n",
" ax.grid(axis=\"y\", which=\"minor\", alpha=0.3)\n",
@@ -310,63 +329,7 @@
" \n",
" ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), prop={'size': 6})\n",
" plt.close()\n",
- " fig.savefig(f\"graphs/{divisor_name}_{kind}_nocomb.png\",dpi=300);"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "df2e236a-4540-4677-a7f1-563c4cc37a3e",
- "metadata": {},
- "source": [
- "Below you can choose a concrete divisor set and visualize it with all the mults, or just some to your liking."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b464865d-b169-446e-a9e7-0cead699aee1",
- "metadata": {},
- "outputs": [],
- "source": [
- "divisor_name = \"powers_of_2_large\"\n",
- "plot_mults = [mult.with_countermeasure(cm) for mult in selected_mults for cm in (None, \"gsr\", \"additive\", \"multiplicative\", \"euclidean\")]\n",
- "plot_divisors = sorted(divisor_map[divisor_name])#[-200:]\n",
- "L = len(plot_divisors)\n",
- "N = len(plot_mults)\n",
- "\n",
- "fig = plt.figure(figsize=(L/4+5, 24))\n",
- "ax = plt.subplot(111)\n",
- "\n",
- "vals = np.zeros((N, L))\n",
- "n_samples = 0\n",
- "for i, mult in enumerate(plot_mults):\n",
- " y_values = [distributions_mults[mult][l] for l in plot_divisors]\n",
- " vals[i,] = y_values\n",
- " ax.plot(list(range(L)), y_values, color=colors[mult], linestyle=styles[mult], marker=markers[mult], label=str(mult))\n",
- " n_samples += distributions_mults[mult].samples\n",
- "\n",
- "ax.set_title(f\"{divisor_name} ({kind})\\nSamples(avg): \" + str(n_samples//N))\n",
- "\n",
- "var = np.var(vals, axis=0)\n",
- "ax.plot(list(range(L)), var / np.max(var), label=\"cross-mult variance (normalized)\", ls=\"--\", lw=2, color=\"black\")\n",
- "\n",
- "ax.set_xlabel(\"divisors\")\n",
- "ax.set_ylabel(\"error probability\")\n",
- "ax.set_yticks(majticks)\n",
- "ax.set_yticks(minticks, minor=True)\n",
- "ax.set_xticks(list(range(L)), plot_divisors, rotation=90)\n",
- "\n",
- "ax.grid(axis=\"y\", which=\"major\", alpha=0.7)\n",
- "ax.grid(axis=\"y\", which=\"minor\", alpha=0.3)\n",
- "ax.grid(axis=\"x\", alpha=0.7)\n",
- "plt.tight_layout()\n",
- "box = ax.get_position()\n",
- "ax.set_position([box.x0, box.y0, box.width * 0.9, box.height])\n",
- "\n",
- "# Put a legend to the right of the current axis\n",
- "ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), prop={'size': 5})\n",
- "plt.show()\n",
- "fig.savefig(f\"graphs/{divisor_name}_{kind}_allmults.png\",dpi=300);"
+ " fig.savefig(f\"graphs/{divisor_name}-{kind}-nocomb{'+ci' if showci else ''}.png\", dpi=300);"
]
},
{
@@ -374,6 +337,7 @@
"id": "4068e7d0-addb-45d0-ba87-e572d4c82fbd",
"metadata": {},
"source": [
+ "## Plots (allmults)\n",
"Now, lets also do plots with allmults for all divisor groups."
]
},
@@ -390,6 +354,7 @@
" plot_divisors = sorted(divisor_map[divisor_name])\n",
" L = len(plot_divisors)\n",
" N = len(plot_mults)\n",
+ " x = list(range(L))\n",
" \n",
" fig = plt.figure(figsize=(L/4+10, 26))\n",
" ax = plt.subplot(111)\n",
@@ -397,21 +362,27 @@
" vals = np.zeros((N, L))\n",
" n_samples = 0\n",
" for i, mult in enumerate(plot_mults):\n",
- " y_values = [distributions_mults[mult][l] for l in plot_divisors]\n",
+ " probmap = distributions_mults[mult]\n",
+ " y_values = [probmap[l] for l in plot_divisors]\n",
" vals[i,] = y_values\n",
- " ax.plot(list(range(L)), y_values, color=colors[mult], linestyle=styles[mult], marker=markers[mult], label=str(mult))\n",
- " n_samples += distributions_mults[mult].samples\n",
+ " ax.plot(x, y_values, color=colors[mult], linestyle=styles[mult], marker=markers[mult], label=str(mult))\n",
+ " if showci:\n",
+ " cis = [conf_interval(p, probmap.samples) for p in y_values]\n",
+ " ci_low = [ci[0] for ci in cis]\n",
+ " ci_high = [ci[1] for ci in cis]\n",
+ " ax.fill_between(x, ci_low, ci_high, color=\"black\", alpha=0.1)\n",
+ " n_samples += probmap.samples\n",
" \n",
" ax.set_title(f\"{divisor_name} ({kind})\\nSamples(avg): \" + str(n_samples//N))\n",
" \n",
" var = np.var(vals, axis=0)\n",
- " ax.plot(list(range(L)), var / np.max(var), label=\"cross-mult variance (normalized)\", ls=\"--\", lw=2, color=\"black\")\n",
+ " ax.plot(x, var / np.max(var), label=\"cross-mult variance (normalized)\", ls=\"--\", lw=2, color=\"black\")\n",
" \n",
" ax.set_xlabel(\"divisors\")\n",
" ax.set_ylabel(\"error probability\")\n",
" ax.set_yticks(majticks)\n",
" ax.set_yticks(minticks, minor=True)\n",
- " ax.set_xticks(list(range(L)), plot_divisors, rotation=90)\n",
+ " ax.set_xticks(x, plot_divisors, rotation=90)\n",
" \n",
" ax.grid(axis=\"y\", which=\"major\", alpha=0.7)\n",
" ax.grid(axis=\"y\", which=\"minor\", alpha=0.3)\n",
@@ -422,7 +393,71 @@
" \n",
" ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), prop={'size': 6})\n",
" plt.close()\n",
- " fig.savefig(f\"graphs/{divisor_name}_{kind}_allmults.png\",dpi=300);"
+ " fig.savefig(f\"graphs/{divisor_name}-{kind}-allmults{'+ci' if showci else ''}.png\", dpi=300);"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "df2e236a-4540-4677-a7f1-563c4cc37a3e",
+ "metadata": {},
+ "source": [
+ "## Interactive plot\n",
+ "Below you can choose a concrete divisor set and visualize it with all the mults, or just some to your liking."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b464865d-b169-446e-a9e7-0cead699aee1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "divisor_name = \"powers_of_2_large\"\n",
+ "plot_mults = [mult.with_countermeasure(cm) for mult in selected_mults for cm in (None, \"gsr\", \"additive\", \"multiplicative\", \"euclidean\")]\n",
+ "plot_divisors = sorted(divisor_map[divisor_name])#[-200:]\n",
+ "L = len(plot_divisors)\n",
+ "N = len(plot_mults)\n",
+ "x = list(range(L))\n",
+ "\n",
+ "fig = plt.figure(figsize=(L/4+5, 24))\n",
+ "ax = plt.subplot(111)\n",
+ "\n",
+ "vals = np.zeros((N, L))\n",
+ "n_samples = 0\n",
+ "for i, mult in enumerate(plot_mults):\n",
+ " probmap = distributions_mults[mult]\n",
+ " y_values = [probmap[l] for l in plot_divisors]\n",
+ " vals[i,] = y_values\n",
+ " ax.plot(x, y_values, color=colors[mult], linestyle=styles[mult], marker=markers[mult], label=str(mult))\n",
+ " if showci:\n",
+ " cis = [conf_interval(p, probmap.samples) for p in y_values]\n",
+ " ci_low = [ci[0] for ci in cis]\n",
+ " ci_high = [ci[1] for ci in cis]\n",
+ " ax.fill_between(x, ci_low, ci_high, color=\"black\", alpha=0.1)\n",
+ " n_samples += probmap.samples\n",
+ "\n",
+ "ax.set_title(f\"{divisor_name} ({kind})\\nSamples(avg): \" + str(n_samples//N))\n",
+ "\n",
+ "var = np.var(vals, axis=0)\n",
+ "ax.plot(x, var / np.max(var), label=\"cross-mult variance (normalized)\", ls=\"--\", lw=2, color=\"black\")\n",
+ "\n",
+ "ax.set_xlabel(\"divisors\")\n",
+ "ax.set_ylabel(\"error probability\")\n",
+ "ax.set_yticks(majticks)\n",
+ "ax.set_yticks(minticks, minor=True)\n",
+ "ax.set_xticks(x, plot_divisors, rotation=90)\n",
+ "\n",
+ "ax.grid(axis=\"y\", which=\"major\", alpha=0.7)\n",
+ "ax.grid(axis=\"y\", which=\"minor\", alpha=0.3)\n",
+ "ax.grid(axis=\"x\", alpha=0.7)\n",
+ "plt.tight_layout()\n",
+ "box = ax.get_position()\n",
+ "ax.set_position([box.x0, box.y0, box.width * 0.9, box.height])\n",
+ "\n",
+ "# Put a legend to the right of the current axis\n",
+ "ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), prop={'size': 5})\n",
+ "plt.show()\n",
+ "fig.savefig(f\"graphs/{divisor_name}-{kind}-allmults{'+ci' if showci else ''}.png\", dpi=300);"
]
},
{