diff options
| author | J08nY | 2025-03-20 16:37:16 +0100 |
|---|---|---|
| committer | J08nY | 2025-04-16 12:25:06 +0200 |
| commit | 3df86dff595cfbf351d280150d5638f0301e92b4 (patch) | |
| tree | 523cff630bbdee116092aafde02e483decee4e6a | |
| parent | a8caf105dc1af45723c975bd6795bb9352024b30 (diff) | |
| download | ECTester-3df86dff595cfbf351d280150d5638f0301e92b4.tar.gz ECTester-3df86dff595cfbf351d280150d5638f0301e92b4.tar.zst ECTester-3df86dff595cfbf351d280150d5638f0301e92b4.zip | |
| -rw-r--r-- | epare/collect_leia.ipynb | 21 | ||||
| -rw-r--r-- | epare/common.py | 7 | ||||
| -rw-r--r-- | epare/visualize.ipynb | 231 |
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);" ] }, { |
