{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Measurement\n",
"\n",
"This notebook showcases how to use **pyecsca** to generate and measure an ECC implementation.\n",
"This example use the ChipWhisperer-Lite board, along with the UFO target board (with an `STM32F3` target on top)\n",
"and a PicoScope 5000 oscilloscope to measure."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Oscilloscope setup\n",
"\n",
"First we will setup the scope. Channel `A` will be used for the power signal, so we\n",
"connect the `MEASURE` SMA plug (on the UFO board) to the scope `A` input via an SMA-BNC cable. Channel\n",
"`B` will be used for the trigger, so we connect a probe to `TP2` point on the UFO board and connect it\n",
"to input `B` on the scope. \n",
"\n",
"\n",
"\n",
"Next we connect to the scope and display its identifier."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pyecsca.sca.scope.picoscope_sdk import PS6000Scope\n",
"\n",
"scope = PS6000Scope()\n",
"scope.open()\n",
"print(scope.get_variant())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we setup the channels, `A` in AC coupling with 0.2 Volt range, `B` in DC coupling with 5 Volt range."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"scope.setup_channel(channel=\"A\", coupling=\"AC\", range=0.2, offset=0.0, enable=True)\n",
"scope.setup_channel(channel=\"B\", coupling=\"DC\", range=5.0, offset=0.0, enable=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we set the frequency and amount of samples. We set 6.4 MHz and 16M samples,\n",
"which should lead to a 3 second capture time (which should cover the long scalar multiplication operation on the chip ~ 2.8s)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"actual_frequency, samples = scope.setup_frequency(frequency=6_400_000, pretrig=500_000, posttrig=15_500_000)\n",
"print(actual_frequency, samples)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we setup the trigger on channel `B`. We also set channel `A` as the channel to capture. In this example we also capture the `B` channel to showcase the dynamic triggering capabilities."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"scope.setup_trigger(channel=\"B\", threshold=0.5, direction=\"rising\", delay=0, timeout=5000, enable=True)\n",
"scope.setup_capture(channel=\"A\", enable=True)\n",
"scope.setup_capture(channel=\"B\", enable=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Device setup\n",
"\n",
"The `STM32F3` UFO target board is used next, we now will generate and build an ECC implementation.\n",
"\n",
""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import tempfile\n",
"\n",
"from os.path import join\n",
"from pyecsca.codegen.common import Platform, DeviceConfiguration\n",
"from pyecsca.codegen.render import render_and_build\n",
"from pyecsca.ec.model import ShortWeierstrassModel\n",
"from pyecsca.ec.mult import LTRMultiplier\n",
"from pyecsca.ec.configuration import *\n",
"\n",
"platform = Platform.STM32F3\n",
"hash_type = HashType.SHA1\n",
"mod_rand = RandomMod.REDUCE\n",
"mult = Multiplication.BASE\n",
"sqr = Squaring.BASE\n",
"red = Reduction.BARRETT\n",
"inv = Inversion.EULER\n",
"\n",
"model = ShortWeierstrassModel()\n",
"coords = model.coordinates[\"projective\"]\n",
"add = coords.formulas[\"add-1998-cmo\"]\n",
"dbl = coords.formulas[\"dbl-1998-cmo\"]\n",
"formulas = [add, dbl]\n",
"scalarmult = LTRMultiplier(add, dbl)\n",
"\n",
"config = DeviceConfiguration(model, coords, formulas, scalarmult, \n",
" hash_type, mod_rand, mult, sqr, red,\n",
" inv, platform, True, True, True)\n",
"\n",
"tmpdir = tempfile.TemporaryDirectory()\n",
"directory, elf_file, hex_file, res = render_and_build(config, tmpdir.name)\n",
"fw = join(tmpdir.name, hex_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we will create a target and flash the implementation on it.\n",
"The target constructor requires to know some parameters of the configuration,\n",
"to be able to communicate with it."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pyecsca.codegen.client import DeviceTarget\n",
"\n",
"target = DeviceTarget(model=config.model, coords=config.coords, platform=config.platform, timeout=10000)\n",
"target.flash(fw)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Measurement\n",
"\n",
"We can now connect to the target, arm the scope and generate a keypair on the target while measuring it,\n",
"then collect the trace."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from time import sleep, time\n",
"from pyecsca.codegen.client import Triggers\n",
"from pyecsca.sca.trace import Trace\n",
"from pyecsca.sca.scope import SampleType\n",
"from pyecsca.ec.params import get_params\n",
"params = get_params(\"secg\", \"secp128r1\", \"projective\")\n",
"\n",
"print(\"Connect\")\n",
"target.connect()\n",
"print(\"Set parameters\")\n",
"target.set_params(params)\n",
"print(\"Set trigger\")\n",
"target.set_trigger(Triggers.keygen)\n",
"print(\"Init PRNG\")\n",
"target.init_prng(b\"\\x12\\x23\")\n",
"\n",
"print(\"ARM scope\")\n",
"scope.arm()\n",
"sleep(5)\n",
"start = time()\n",
"priv, pub = target.generate()\n",
"end = time()\n",
"print(end - start)\n",
"scope.capture(10000)\n",
"\n",
"print(\"Retrieve\")\n",
"trace = scope.retrieve(\"A\", SampleType.Volt)\n",
"trig = scope.retrieve(\"B\", SampleType.Volt)\n",
"\n",
"print(\"Disconnect\")\n",
"target.disconnect()\n",
"\n",
"print(priv)\n",
"print(pub)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"After all measurements are done, we disconnect from the scope. And delete the directory\n",
"with the firmware."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"target.scope.dis()\n",
"scope.close()\n",
"tmpdir.cleanup()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualization\n",
"\n",
"We will now visualize the trace."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pyecsca.sca.trace.plot import plot_traces\n",
"import holoviews as hv\n",
"\n",
"hv.extension(\"bokeh\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plot_traces(trace, trig).opts(width=950, height=600)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"@webio": {
"lastCommId": "57001c30f0b44a2b8c6e1ed2455e6df9",
"lastKernelId": "c41aa10d-5e94-483e-a64d-be40efd99bcc"
},
"hide_input": false,
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
},
"latex_envs": {
"LaTeX_envs_menu_present": true,
"autoclose": false,
"autocomplete": true,
"bibliofile": "biblio.bib",
"cite_by": "apalike",
"current_citInitial": 1,
"eqLabelWithNumbers": true,
"eqNumInitial": 1,
"hotkeys": {
"equation": "Ctrl-E",
"itemize": "Ctrl-I"
},
"labels_anchors": false,
"latex_user_defs": false,
"report_style_numbering": false,
"user_envs_cfg": false
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}