{ "cells": [ { "cell_type": "markdown", "id": "fde96d6a-0281-4ba2-ae2f-447cb6a625f0", "metadata": {}, "source": [ "# 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." ] }, { "cell_type": "code", "execution_count": null, "id": "eab24cb8-af40-4cc1-be5f-f120fe9932ee", "metadata": {}, "outputs": [], "source": [ "from pyecsca.sca.target.ectester import ECTesterTargetLEIA, KeypairEnum, ParameterEnum, CurveEnum, KeyEnum, KeyClassEnum, KeyBuildEnum, KeyAgreementEnum, SignatureEnum, TransformationEnum\n", "from pyecsca.ec.params import load_params_ectester, get_params\n", "from pyecsca.sca.scope.picoscope_sdk import PS6000Scope\n", "from pyecsca.sca.trace import Trace\n", "from pyecsca.sca.trace.plot import plot_trace, plot_traces\n", "from pyecsca.sca.scope import SampleType\n", "\n", "import numpy as np\n", "from time import sleep\n", "from smartleia import LEIA, TriggerPoints\n", "\n", "import holoviews as hv\n", "\n", "hv.extension(\"bokeh\")\n", "%opts RGB [height=700, responsive=True]" ] }, { "cell_type": "code", "execution_count": null, "id": "3843d504-074a-4736-8c42-114ec63dab7d", "metadata": {}, "outputs": [], "source": [ "from pyecsca.sca.trace.sampling import downsample_max, downsample_average, downsample_decimate\n", "from pyecsca.sca.trace.process import rolling_mean, recenter, absolute, threshold\n", "from pyecsca.sca.trace.filter import filter_lowpass\n", "from pyecsca.sca.trace.edit import pad, trim" ] }, { "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", "metadata": {}, "outputs": [], "source": [ "sl = LEIA()\n", "ectester = ECTesterTargetLEIA(sl)" ] }, { "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.\n", "\n", "Note that several smartcards really are the same model and thus we merged them:\n", " - N4 = N5 = N10\n", " - N6 = N7\n", " - N3 = N8" ] }, { "cell_type": "code", "execution_count": null, "id": "07369045-7eee-4e85-ac4e-37e5396fd880", "metadata": {}, "outputs": [], "source": [ "ectester.connect()" ] }, { "cell_type": "code", "execution_count": null, "id": "89ed5db7-fbbb-4b32-89a5-6486204feed6", "metadata": {}, "outputs": [], "source": [ "atr = ectester.atr\n", "print(atr, atr.hex())\n", "card_map = {\n", " \"3bd518ff8191fe1fc38073c8211309\": \"A1\",\n", " \"3bb89600c00831fe45ffff1154305023006a\": \"I1\",\n", " \"3bfe1800008031fe45803180664090a5102e1083019000f2\": \"I2\",\n", " \"3bf81800ff8131fe454a434f507632343143\": \"N1\",\n", " \"3bf81300008131fe454a434f5076323431b7\": \"N2N9\",\n", " \"3b9495810146545601c4\": \"N4N10\",\n", " \"3bd518ff8191fe1fc38073c821100a\": \"N6N11\",\n", " \"3bf91300008131fe454a434f503234325233a2\":\"N8\",\n", " \"3b9c9580811f039067464a01005404f272fe00c0\": \"F1\",\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)" ] }, { "cell_type": "code", "execution_count": null, "id": "8c33b783-7a7e-45f4-9e91-d3df54e1abe0", "metadata": {}, "outputs": [], "source": [ "scope = PS6000Scope()" ] }, { "cell_type": "code", "execution_count": null, "id": "729688c3-d448-4d1a-80c4-666680fcca35", "metadata": {}, "outputs": [], "source": [ "scope.open()" ] }, { "cell_type": "code", "execution_count": null, "id": "23037143-e39c-4dd6-bf65-9cdcb0d7b266", "metadata": {}, "outputs": [], "source": [ "print(scope.get_variant())\n", "if card == \"A1\":\n", " # Athena IDProtect\n", " # 35M for keygen\n", " # 13M for ecdh\n", " actual_freq, n_samples = scope.setup_frequency(frequency=50_000_000, pretrig=0, posttrig=35_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.24, 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 == \"I1\":\n", " # Infineon SECORA\n", " # 6M for keygen\n", " # 3M for ecdh\n", " actual_freq, n_samples = scope.setup_frequency(frequency=50_000_000, pretrig=0, posttrig=6_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.25, 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 == \"I2\":\n", " # Infineon CJTOP SLJ 52GLA0890AL M84\n", " actual_freq, n_samples = scope.setup_frequency(frequency=50_000_000, pretrig=0, posttrig=15_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.1, offset=-0.15, 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 == \"N1\":\n", " # NXP J3A081\n", " # 30M for keygen (first), then 10M for subsequent\n", " # 10M for ecdh\n", " actual_freq, n_samples = scope.setup_frequency(frequency=50_000_000, pretrig=0, posttrig=30_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.05, 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 == \"N2N9\":\n", " # NXP JCOP v2.4.1R3\n", " # N9\n", " actual_freq, n_samples = scope.setup_frequency(frequency=625_000_000, pretrig=0, posttrig=500_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.05, offset=-0.280, 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", " # N2\n", " #actual_freq, n_samples = scope.setup_frequency(frequency=50_000_000, pretrig=0, posttrig=40_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.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 == \"N4N10\":\n", " # NXP J3H145\n", " # 15M for keygen\n", " # 10M for ecdh\n", " actual_freq, n_samples = scope.setup_frequency(frequency=50_000_000, pretrig=0, posttrig=5_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.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 == \"N6N11\":\n", " # NXP JCOP4\n", " # 3M for keygen\n", " # 3M for ECDH\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 == \"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 == \"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)" ] }, { "cell_type": "code", "execution_count": null, "id": "a16af658-34d5-455e-a609-3075ede7dad7", "metadata": {}, "outputs": [], "source": [ "ectester.select_applet()" ] }, { "cell_type": "code", "execution_count": null, "id": "ee3ec16b-d9ac-4cd7-8ad5-4dfe0c0d5465", "metadata": {}, "outputs": [], "source": [ "ectester.info()" ] }, { "cell_type": "markdown", "id": "088f806f-63ad-432a-a654-0d8a7d857187", "metadata": {}, "source": [ "## 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." ] }, { "cell_type": "code", "execution_count": null, "id": "27c4a7f7-3029-46d9-a3d5-3f4c0b56e37e", "metadata": {}, "outputs": [], "source": [ "ectester.allocate(KeypairEnum.KEYPAIR_LOCAL,\n", " KeyBuildEnum.BUILD_KEYBUILDER | KeyBuildEnum.BUILD_KEYPAIR,\n", " 256,\n", " KeyClassEnum.ALG_EC_FP)" ] }, { "cell_type": "code", "execution_count": null, "id": "5b03d70f-1bf1-4e74-b9e4-b9d1591baee5", "metadata": {}, "outputs": [], "source": [ "ectester.allocate_sig(SignatureEnum.ALG_ECDSA_SHA)" ] }, { "cell_type": "code", "execution_count": null, "id": "f952cf17-88f2-47d7-88f3-346c6cbd0572", "metadata": {}, "outputs": [], "source": [ "ectester.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH)" ] }, { "cell_type": "markdown", "id": "74a56989-91e7-44c8-ba9c-4c387c41de86", "metadata": {}, "source": [ "## Set params\n", "\n", "Set a curve (domain parameters) on the keypair on the card." ] }, { "cell_type": "code", "execution_count": null, "id": "4d67e5a3-79d3-4160-89c9-07f1cd697f52", "metadata": {}, "outputs": [], "source": [ "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\")" ] }, { "cell_type": "code", "execution_count": null, "id": "94e9c2c5-d231-459a-a601-b14a123fce62", "metadata": {}, "outputs": [], "source": [ "ectester.set(KeypairEnum.KEYPAIR_LOCAL,\n", " CurveEnum.external,\n", " ParameterEnum.DOMAIN_FP,\n", " ECTesterTargetLEIA.encode_parameters(ParameterEnum.DOMAIN_FP, params))" ] }, { "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", "metadata": {}, "outputs": [], "source": [ "sl.set_trigger_strategy(1, point_list=[TriggerPoints.TRIG_PRE_SEND_APDU], delay=0)" ] }, { "cell_type": "markdown", "id": "7cd21c6a-e741-4e60-98ae-f7f58fa70a02", "metadata": {}, "source": [ "## Generate\n", "Generate a keypair and capture a trace of it." ] }, { "cell_type": "code", "execution_count": null, "id": "26be2fb8-607c-4bce-8968-ba57938f9a89", "metadata": {}, "outputs": [], "source": [ "scope.arm()\n", "sleep(2)" ] }, { "cell_type": "code", "execution_count": null, "id": "83bfe8a5-00eb-4b17-8762-1ba0b07139e6", "metadata": {}, "outputs": [], "source": [ "ectester.generate(KeypairEnum.KEYPAIR_LOCAL)" ] }, { "cell_type": "code", "execution_count": null, "id": "253a00c7-28d3-4648-b5d7-9c6876f37362", "metadata": {}, "outputs": [], "source": [ "scope.capture(10000)" ] }, { "cell_type": "code", "execution_count": null, "id": "2a0836a3-9d07-444c-a4eb-a661885ff3e8", "metadata": {}, "outputs": [], "source": [ "trace_gen = scope.retrieve(\"B\", SampleType.Volt)" ] }, { "cell_type": "code", "execution_count": null, "id": "f14f244d-fee6-44a9-8858-783d7ffb9f24", "metadata": {}, "outputs": [], "source": [ "plot_trace(trace_gen)" ] }, { "cell_type": "code", "execution_count": null, "id": "dd2a71ea-02ed-40d9-9268-6b47f304bac1", "metadata": {}, "outputs": [], "source": [ "plot_trace(downsample_average(trace_gen, 1000))" ] }, { "cell_type": "markdown", "id": "f795ea2f-c244-435f-b040-9c7e9ef9debd", "metadata": {}, "source": [ "## Or set key\n", "Capture a trace of the set-key operation on the keypair. This is interesting as some precomputation may happen." ] }, { "cell_type": "code", "execution_count": null, "id": "9d3daeda-55d4-43d2-b33f-5c9caba41e67", "metadata": {}, "outputs": [], "source": [ "priv = 0x3c984f3a459a6b8f1a5ece87a695d1b112b978024a9c56c1a12ade3500f29d8c\n", "pub = params.curve.affine_multiply(params.generator, priv + 5)" ] }, { "cell_type": "code", "execution_count": null, "id": "3f12952f-e9fd-44cf-817e-813385306145", "metadata": {}, "outputs": [], "source": [ "scope.arm()\n", "sleep(2)" ] }, { "cell_type": "code", "execution_count": null, "id": "34457f1e-de06-480e-9668-ef903f6d2b8c", "metadata": {}, "outputs": [], "source": [ "ectester.set(KeypairEnum.KEYPAIR_LOCAL,\n", " CurveEnum.external,\n", " ParameterEnum.S | ParameterEnum.W,\n", " {**ECTesterTargetLEIA.encode_parameters(ParameterEnum.S, priv),\n", " **ECTesterTargetLEIA.encode_parameters(ParameterEnum.W, pub)})" ] }, { "cell_type": "code", "execution_count": null, "id": "a5117598-8cee-4af8-89f1-27af2360bd96", "metadata": {}, "outputs": [], "source": [ "scope.capture(10000)" ] }, { "cell_type": "code", "execution_count": null, "id": "f8b68498-359a-47bd-ac20-c298b933cda7", "metadata": {}, "outputs": [], "source": [ "trace_set = scope.retrieve(\"B\", SampleType.Volt)" ] }, { "cell_type": "code", "execution_count": null, "id": "0b2f0bb8-87a2-400a-90b3-b47b4181721b", "metadata": {}, "outputs": [], "source": [ "plot_trace(trace_set)" ] }, { "cell_type": "markdown", "id": "2037aa6a-3533-4f20-b618-154aa040892c", "metadata": {}, "source": [ "## ECDSA\n", "Perform an ECDSA signature and capture a trace of it." ] }, { "cell_type": "code", "execution_count": null, "id": "2af07d2a-d005-4a75-9a71-f2cd0e723335", "metadata": {}, "outputs": [], "source": [ "scope.arm()\n", "sleep(2)" ] }, { "cell_type": "code", "execution_count": null, "id": "c49b3f74-6229-463d-a4eb-468d7d98d4e4", "metadata": {}, "outputs": [], "source": [ "resp = ectester.ecdsa_sign(KeypairEnum.KEYPAIR_LOCAL,\n", " True,\n", " SignatureEnum.ALG_ECDSA_SHA,\n", " b\"message\")\n", "resp" ] }, { "cell_type": "code", "execution_count": null, "id": "b5078978-5867-438b-a4e1-7e4cc35d32e6", "metadata": {}, "outputs": [], "source": [ "scope.capture(10000)" ] }, { "cell_type": "code", "execution_count": null, "id": "1b7d93be-78df-44a5-ae04-5db99db5a099", "metadata": {}, "outputs": [], "source": [ "trace_ecdsa = scope.retrieve(\"B\", SampleType.Volt)" ] }, { "cell_type": "code", "execution_count": null, "id": "0abe66cf-b10f-49b8-a525-e606136303de", "metadata": {}, "outputs": [], "source": [ "plot_trace(trace_ecdsa)" ] }, { "cell_type": "markdown", "id": "78f9c17b-c195-4c70-a5b7-79337de3112a", "metadata": {}, "source": [ "## ECDH\n", "Perform ECDH and capture a trace of it." ] }, { "cell_type": "code", "execution_count": null, "id": "08978417-6f39-4c40-a862-9ad43179dccc", "metadata": {}, "outputs": [], "source": [ "scope.arm()\n", "sleep(2)" ] }, { "cell_type": "code", "execution_count": null, "id": "d7626f39-53e4-41a7-afe2-d89e15f6a0c9", "metadata": {}, "outputs": [], "source": [ "ectester.ecdh_direct(KeypairEnum.KEYPAIR_LOCAL,\n", " True,\n", " TransformationEnum.NONE,\n", " KeyAgreementEnum.ALG_EC_SVDP_DH,\n", " bytes(params.generator))" ] }, { "cell_type": "code", "execution_count": null, "id": "2282c28d-4745-43b2-b321-a34365f55753", "metadata": {}, "outputs": [], "source": [ "scope.capture(10000)" ] }, { "cell_type": "code", "execution_count": null, "id": "d0cdabb8-a4ee-4b19-a8ed-ae2e9f634a37", "metadata": {}, "outputs": [], "source": [ "trace_ecdh = scope.retrieve(\"B\", SampleType.Volt)" ] }, { "cell_type": "code", "execution_count": null, "id": "c899b299-25f5-418b-baaa-fa8e4399daa9", "metadata": {}, "outputs": [], "source": [ "plot_trace(trace_ecdh)" ] }, { "cell_type": "markdown", "id": "cadca3b4-2cd9-4dab-a900-18fe90a7cbe6", "metadata": {}, "source": [ "## Cleanup\n", "Cleanup some memory on the card, disconnect from it and the scope." ] }, { "cell_type": "code", "execution_count": null, "id": "a29e460b-cca7-4d28-b2e2-3920b0870628", "metadata": {}, "outputs": [], "source": [ "ectester.cleanup()" ] }, { "cell_type": "code", "execution_count": null, "id": "264b4653-023c-4a39-8970-270c2f0d42c6", "metadata": {}, "outputs": [], "source": [ "ectester.disconnect()" ] }, { "cell_type": "code", "execution_count": null, "id": "1d706782-7124-4879-8198-407e45f131ff", "metadata": {}, "outputs": [], "source": [ "scope.close()" ] }, { "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": [ "### Frequency analysis" ] }, { "cell_type": "code", "execution_count": null, "id": "60139124-f51c-4e1c-858c-e46731d38bde", "metadata": {}, "outputs": [], "source": [ "plot_trace(filter_lowpass(trim(trace_ecdsa, 55_000_000, 56_000_000), 625000000, 250_000_000))" ] }, { "cell_type": "code", "execution_count": null, "id": "031d0c18-7408-46e0-8853-cc9e485902d1", "metadata": {}, "outputs": [], "source": [ "from scipy.signal import periodogram\n", "from matplotlib import pyplot as plt" ] }, { "cell_type": "code", "execution_count": null, "id": "40be47b6-b08a-49e2-b40e-2b5b6ca6be13", "metadata": {}, "outputs": [], "source": [ "%matplotlib widget" ] }, { "cell_type": "code", "execution_count": null, "id": "65aa1eb2-2694-4697-a2cd-869272218b6a", "metadata": {}, "outputs": [], "source": [ "f, Pxx_den = periodogram(recenter(trim(trace_ecdsa, 30_000_000, 500_000_000)).samples, 625000000)" ] }, { "cell_type": "code", "execution_count": null, "id": "c52d798b-d40b-4914-908a-3b34c51a2084", "metadata": {}, "outputs": [], "source": [ "plt.semilogy(f, Pxx_den)\n", "#plt.ylim([1e-7, 1e2])\n", "plt.xlabel('frequency [Hz]')\n", "plt.ylabel('PSD [V**2/Hz]')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "197fd1b9-7d91-4f23-b89d-e68727be36d7", "metadata": {}, "outputs": [], "source": [ "from scipy.signal import find_peaks" ] }, { "cell_type": "code", "execution_count": null, "id": "1fd46ee6-de5e-45f6-bee0-e3ead0029aca", "metadata": {}, "outputs": [], "source": [ "pks = find_peaks(Pxx_den, height=1.52e-7)" ] }, { "cell_type": "code", "execution_count": null, "id": "3b03f991-7f89-43df-9726-13515ae78c26", "metadata": {}, "outputs": [], "source": [ "len(pks[0])\n", "for pk in f[pks[0]]:\n", " print(int(pk))" ] }, { "cell_type": "code", "execution_count": null, "id": "d4f527fe-96f3-4e90-b802-3d063beb619c", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "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.13.7" } }, "nbformat": 4, "nbformat_minor": 5 }