{ "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", "![measurement setup](img/measurement_setup.jpg)\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", "![measurement ufo](img/measurement_ufo.jpg)" ] }, { "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 }