aboutsummaryrefslogtreecommitdiff
path: root/analysis/countermeasures/collect_leia.ipynb
diff options
context:
space:
mode:
Diffstat (limited to 'analysis/countermeasures/collect_leia.ipynb')
-rw-r--r--analysis/countermeasures/collect_leia.ipynb194
1 files changed, 125 insertions, 69 deletions
diff --git a/analysis/countermeasures/collect_leia.ipynb b/analysis/countermeasures/collect_leia.ipynb
index 4399c47..5cdbd51 100644
--- a/analysis/countermeasures/collect_leia.ipynb
+++ b/analysis/countermeasures/collect_leia.ipynb
@@ -5,7 +5,11 @@
"id": "fde96d6a-0281-4ba2-ae2f-447cb6a625f0",
"metadata": {},
"source": [
- "# Power-tracing smartcards using LEIA"
+ "# Power-tracing smartcards using LEIA\n",
+ "\n",
+ "This notebook uses [**pyecsca**](https://pyecsca.org) and a [LEIA board](https://github.com/h2lab/smartleia) to do simple (SPA-like) power-tracing of selected smartcard targets. It assumess the user has a PicoScope 6000 oscilloscope (though this can be replaced by any oscilloscope that **pyecsca** supports). Similarly, the LEIA board can be replaced by simple smartcard reader, though the user then need a separate way of triggering the scope. The code also assumes the ECTester applet is already installed on the target cards. Use a tool like [GlobalPlatformPro](https://github.com/martinpaljak/GlobalPlatformPro) to install it.\n",
+ "\n",
+ "See the [pyecsca notebook](https://pyecsca.org/notebook/measurement.html) on measurement for more examples."
]
},
{
@@ -46,6 +50,14 @@
]
},
{
+ "cell_type": "markdown",
+ "id": "6951ffd4-b442-4180-b278-692950b1979a",
+ "metadata": {},
+ "source": [
+ "Create the LEIA interface and ECTester target."
+ ]
+ },
+ {
"cell_type": "code",
"execution_count": null,
"id": "865953a5-35ad-473e-a57f-f26368145987",
@@ -57,6 +69,15 @@
]
},
{
+ "cell_type": "markdown",
+ "id": "99aa64e8-2534-4db7-8cc5-73268bafa884",
+ "metadata": {},
+ "source": [
+ "## Initialize scope and card\n",
+ "Connect to the card and setup the scope/measurement parameters based on it."
+ ]
+ },
+ {
"cell_type": "code",
"execution_count": null,
"id": "07369045-7eee-4e85-ac4e-37e5396fd880",
@@ -81,11 +102,15 @@
" \"3bfe1800008031fe45803180664090a5102e1083019000f2\": \"I2\",\n",
" \"3bf81800ff8131fe454a434f507632343143\": \"N1\",\n",
" \"3bf81300008131fe454a434f5076323431b7\": \"N2N9\",\n",
- " \"3b9495810146545601c4\": \"N4\",\n",
- " \"3bd518ff8191fe1fc38073c821100a\": \"N6\",\n",
+ " \"3b9495810146545601c4\": \"N4N10\",\n",
+ " \"3bd518ff8191fe1fc38073c821100a\": \"N6N11\",\n",
+ " \"3bf91300008131fe454a434f503234325233a2\":\"N8\",\n",
" \"3b9c9580811f039067464a01005404f272fe00c0\": \"F1\",\n",
- " \"3b90958011fe6a\": \"F2\",\n",
- " \"3b9f95803fc7a08031e073fa21106300000083f09000bb\": \"S1S2\"\n",
+ " \"3b90958011fe6a\": \"F2F3\",\n",
+ " \"3b9f95803fc7a08031e073fa21106300000083f09000bb\": \"S1S2\",\n",
+ " \"3bf99600008131fe4553434537200e00202028\":\"G1\",\n",
+ " \"3bfe1800008031fe4553434536302d43443038312d6e46a9\": \"G2\",\n",
+ " \"3b959540ffae01030000\":\"E2\",\n",
"}\n",
"card = card_map.get(atr.hex(), None)\n",
"print(card)"
@@ -167,7 +192,7 @@
" #scope.setup_channel(channel=\"B\", coupling=\"DC_50\", range=0.1, offset=-0.18, 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 == \"N4\":\n",
+ "elif card == \"N4N10\":\n",
" # NXP J3H145\n",
" # 15M for keygen\n",
" # 10M for ecdh\n",
@@ -176,7 +201,7 @@
" scope.setup_channel(channel=\"B\", coupling=\"DC_50\", range=0.2, offset=-0.160, 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 == \"N6\":\n",
+ "elif card == \"N6N11\":\n",
" # NXP JCOP4\n",
" # 3M for keygen\n",
" # 3M for ECDH\n",
@@ -192,13 +217,15 @@
" 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",
+ "elif card == \"F2F3\":\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",
+ "else:\n",
+ " print(\"Unkown card, set the appropriate parameters manually.\")\n",
"print(actual_freq, n_samples)"
]
},
@@ -227,7 +254,10 @@
"id": "088f806f-63ad-432a-a654-0d8a7d857187",
"metadata": {},
"source": [
- "## Allocate"
+ "## Allocate\n",
+ "Allocate a keypair and the signature + key exchange objects on the card.\n",
+ "\n",
+ "If any of these fail. Try running `ectester.cleanup()` which frees some memory on the card."
]
},
{
@@ -268,7 +298,9 @@
"id": "74a56989-91e7-44c8-ba9c-4c387c41de86",
"metadata": {},
"source": [
- "## Set params"
+ "## Set params\n",
+ "\n",
+ "Set a curve (domain parameters) on the keypair on the card."
]
},
{
@@ -278,8 +310,10 @@
"metadata": {},
"outputs": [],
"source": [
- "#params = get_params(\"secg\", \"secp256r1\", \"affine\")\n",
- "params = load_params_ectester(\"../countermeasures/countermeasures/tests/comb/cofactor256p18446744073709551617_smallgen_fakeorder.csv\", \"affine\")"
+ "params = get_params(\"secg\", \"secp256r1\", \"affine\")\n",
+ "\n",
+ "# or set parameters that will lead to EPA issues\n",
+ "# params = load_params_ectester(\"../countermeasures/countermeasures/tests/comb/cofactor256p18446744073709551617_smallgen_fakeorder.csv\", \"affine\")"
]
},
{
@@ -296,6 +330,14 @@
]
},
{
+ "cell_type": "markdown",
+ "id": "e9e6a1a7-3d06-44eb-b247-274e0554b1ed",
+ "metadata": {},
+ "source": [
+ "Enable the LEIA trigger on APDU send."
+ ]
+ },
+ {
"cell_type": "code",
"execution_count": null,
"id": "8d36f397-15ca-4e4a-8a87-dee10fe4398e",
@@ -310,7 +352,8 @@
"id": "7cd21c6a-e741-4e60-98ae-f7f58fa70a02",
"metadata": {},
"source": [
- "## Generate"
+ "## Generate\n",
+ "Generate a keypair and capture a trace of it."
]
},
{
@@ -375,61 +418,12 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
- "id": "e6a835a5-5904-4ec4-891a-ea4b2a57f1c9",
- "metadata": {},
- "outputs": [],
- "source": [
- "def find_bumps(trace):\n",
- " ds = downsample_average(trace, 1000)\n",
- " ts = threshold(ds, 0.025)\n",
- " prev = 0\n",
- " previ = 0\n",
- " total = []\n",
- " big = []\n",
- " for i, sample in enumerate(ts.samples):\n",
- " if prev == 0 and sample == 1:\n",
- " dist = i - previ\n",
- " if dist > 2500 and total:\n",
- " big.append(total)\n",
- " total = []\n",
- " if dist > 500:\n",
- " l = [i]\n",
- " total.append(l)\n",
- " else:\n",
- " total[-1].append(i)\n",
- " previ = i\n",
- " prev = sample\n",
- " elif prev == 1 and sample == 0:\n",
- " prev = sample\n",
- " if total:\n",
- " big.append(total)\n",
- " s = []\n",
- " for t in big:\n",
- " seq = []\n",
- " for l in t:\n",
- " seq.append(str(len(l)))\n",
- " s.append(\",\".join(seq))\n",
- " return \"-\".join(s)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3bfb4b6b-f44d-43a4-b52f-fad6f978c301",
- "metadata": {},
- "outputs": [],
- "source": [
- "find_bumps(trace_gen)"
- ]
- },
- {
"cell_type": "markdown",
"id": "f795ea2f-c244-435f-b040-9c7e9ef9debd",
"metadata": {},
"source": [
- "## Or set key"
+ "## Or set key\n",
+ "Capture a trace of the set-key operation on the keypair. This is interesting as some precomputation may happen."
]
},
{
@@ -503,7 +497,8 @@
"id": "2037aa6a-3533-4f20-b618-154aa040892c",
"metadata": {},
"source": [
- "## ECDSA"
+ "## ECDSA\n",
+ "Perform an ECDSA signature and capture a trace of it."
]
},
{
@@ -566,7 +561,8 @@
"id": "78f9c17b-c195-4c70-a5b7-79337de3112a",
"metadata": {},
"source": [
- "## ECDH"
+ "## ECDH\n",
+ "Perform ECDH and capture a trace of it."
]
},
{
@@ -626,10 +622,11 @@
},
{
"cell_type": "markdown",
- "id": "309e8d83-771b-4783-9bb5-474c9d5bf8ec",
+ "id": "cadca3b4-2cd9-4dab-a900-18fe90a7cbe6",
"metadata": {},
"source": [
- "## Misc"
+ "## Cleanup\n",
+ "Cleanup some memory on the card, disconnect from it and the scope."
]
},
{
@@ -664,6 +661,65 @@
},
{
"cell_type": "markdown",
+ "id": "309e8d83-771b-4783-9bb5-474c9d5bf8ec",
+ "metadata": {},
+ "source": [
+ "## Misc\n",
+ "Some miscellaneous remains of previous experiments: 🪦"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e6a835a5-5904-4ec4-891a-ea4b2a57f1c9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def find_bumps(trace):\n",
+ " ds = downsample_average(trace, 1000)\n",
+ " ts = threshold(ds, 0.025)\n",
+ " prev = 0\n",
+ " previ = 0\n",
+ " total = []\n",
+ " big = []\n",
+ " for i, sample in enumerate(ts.samples):\n",
+ " if prev == 0 and sample == 1:\n",
+ " dist = i - previ\n",
+ " if dist > 2500 and total:\n",
+ " big.append(total)\n",
+ " total = []\n",
+ " if dist > 500:\n",
+ " l = [i]\n",
+ " total.append(l)\n",
+ " else:\n",
+ " total[-1].append(i)\n",
+ " previ = i\n",
+ " prev = sample\n",
+ " elif prev == 1 and sample == 0:\n",
+ " prev = sample\n",
+ " if total:\n",
+ " big.append(total)\n",
+ " s = []\n",
+ " for t in big:\n",
+ " seq = []\n",
+ " for l in t:\n",
+ " seq.append(str(len(l)))\n",
+ " s.append(\",\".join(seq))\n",
+ " return \"-\".join(s)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3bfb4b6b-f44d-43a4-b52f-fad6f978c301",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "find_bumps(trace_gen)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
"id": "a5ed4ed6-7732-41a6-b6a9-61e76851f468",
"metadata": {},
"source": [
@@ -782,7 +838,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.12.3"
+ "version": "3.13.5"
}
},
"nbformat": 4,