Skip to content

Commit

Permalink
[otbn/ecdsa] Add capture support for ECDSA-256/384
Browse files Browse the repository at this point in the history
Initial support to capture side-channel traces from
OTBN ECDSA-256 and ECDSA-384 applications.

New capture commands `ecdsa-simple` and `ecdsa-stream`.

Supports only CW-Husky as the capture device.

* New files:
   * `cw/capture_ecdsa_cw310.yaml`: config file for ECDSA-256 experiments
   * `cw/capture_ecdsa384_cw310.yaml`: config file for ECDSA-384 experiments
   * `cw/objs/lowrisc_systems_chip_earlgrey_cw310_0.1_ecdsa.bit`:
example FPGA bitstream
   * `cw/objs/ecc384_serial_fpga_cw310.bin`: example ECDSA-384 driver
   * `cw/objs/ecc_serial_fpga_cw310.bin`: example ECDSA-256 driver

* Modified files:
   * `cw/capture.py`
      * Add new command `ecdsa-simple` to collect long ECDSA traces without using CW-Husky's streaming mode
      * Add new command `ecdsa-stream` to collect long ECDSA traces using CW-Husky's streaming mode
   * `cw/device.py`
      * Changes to handle PLL frequencies other than 100MHZ
      * Add `program_target()` for programming the firmware during ECDSA
experiments.

* Notes:
   * Tested on CW-310 with a CW-Husky

Signed-off-by: Bilgiday Yuce <bilgiday@opentitan.org>
  • Loading branch information
bilgiday authored and vrozic committed Aug 2, 2022
1 parent de113cf commit f51c6ec
Show file tree
Hide file tree
Showing 8 changed files with 466 additions and 13 deletions.
345 changes: 344 additions & 1 deletion cw/capture.py

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions cw/capture_ecdsa384_cw310.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
device:
fpga_bitstream: objs/lowrisc_systems_chip_earlgrey_cw310_0.1_ecdsa.bit
fw_bin: objs/ecc384_serial_fpga_cw310.bin
# Target operating frequency
pll_frequency: 22000000
baudrate: 115200
capture:
# Only ECDSA-256 (32) and ECDSA-384 (48) are supported at this moment.
key_len_bytes: 48
plain_text_len_bytes: 48
output_len_bytes: 48
# Samples per trace
num_samples: 262140
# Offset in samples
offset: 0
# scope gain in db - 13 for non-stream mode, 32.5 for stream mode
scope_gain: 32.5
num_traces: 1
project_name: projects/opentitan_ecdsa_384
waverunner_ip: 192.168.1.228
batch_prng_seed: 0
# As OTBN operations are long, we may need to
# overwrite the folloing parameters
adc_mul: 1
decimate: 1
plot_capture:
show: true
num_traces: 1
trace_image_filename: projects/sample_traces_ecdsa384.html
29 changes: 29 additions & 0 deletions cw/capture_ecdsa_cw310.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
device:
fpga_bitstream: objs/lowrisc_systems_chip_earlgrey_cw310_0.1_ecdsa.bit
fw_bin: objs/ecc_serial_fpga_cw310.bin
# Target operating frequency
pll_frequency: 22000000
baudrate: 115200
capture:
# Only ECDSA-256 (32) and ECDSA-384 (48) are supported at this moment.
key_len_bytes: 32
plain_text_len_bytes: 32
output_len_bytes: 32
# Samples per trace
num_samples: 262140
# Offset in samples
offset: 0
# scope gain in db - 13 for non-stream mode, 32.5 for stream mode
scope_gain: 32.5
num_traces: 1
project_name: projects/opentitan_ecdsa_256
waverunner_ip: 192.168.1.228
batch_prng_seed: 0
# As OTBN operations are long, we may need to
# overwrite the folloing parameters
adc_mul: 1
decimate: 1
plot_capture:
show: true
num_traces: 1
trace_image_filename: projects/sample_traces_ecdsa256.html
Binary file added cw/objs/ecc384_serial_fpga_cw310.bin
Binary file not shown.
Binary file added cw/objs/ecc_serial_fpga_cw310.bin
Binary file not shown.
Binary file not shown.
74 changes: 63 additions & 11 deletions cw/util/device.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

r"""CW305 utility functions. Used to configure FPGA with OpenTitan design."""

import inspect
Expand All @@ -19,6 +18,7 @@ class RuntimePatchFPGAProgram:
This class can be used to detect if the FPGA was actually programmed or not.
"""

def __init__(self, fpga, callback):
"""Inits a RuntimePatchFPGAProgram.
Expand All @@ -31,18 +31,21 @@ def __init__(self, fpga, callback):
self._orig_fn = fpga.FPGAProgram

def __enter__(self):

def wrapped_fn(*args, **kwargs):
self._callback()
return self._orig_fn(*args, **kwargs)

self._fpga.FPGAProgram = wrapped_fn

def __exit__(self, exc_type, exc_value, traceback):
self._fpga.FPGAProgram = self._orig_fn


class OpenTitan(object):
def __init__(self, bitstream, firmware, pll_frequency, baudrate, scope_gain,
num_samples, offset, output_len):

def __init__(self, bitstream, firmware, pll_frequency, baudrate,
scope_gain, num_samples, offset, output_len):

# Extract target board type from bitstream name.
m = re.search('cw305|cw310', bitstream)
Expand All @@ -54,11 +57,17 @@ def __init__(self, bitstream, firmware, pll_frequency, baudrate, scope_gain,
fpga = cw.capture.targets.CW310()
programmer = SpiProgrammer(fpga)
else:
raise ValueError('Could not infer target board type from bistream name')
raise ValueError(
'Could not infer target board type from bistream name')

# Added `pll_frequency` to handle frequencies other than 100MHz.
# Needed this for OTBN ECDSA.
# TODO: Remove these comments after discussion
self.fpga = self.initialize_fpga(fpga, bitstream, pll_frequency)
self.scope = self.initialize_scope(scope_gain, num_samples, offset)
self.target = self.initialize_target(programmer, firmware, baudrate, output_len)
self.scope = self.initialize_scope(scope_gain, num_samples, offset,
pll_frequency)
self.target = self.initialize_target(programmer, firmware, baudrate,
output_len, pll_frequency)

def initialize_fpga(self, fpga, bitstream, pll_frequency):
"""Initializes FPGA bitstream and sets PLL frequency."""
Expand Down Expand Up @@ -97,6 +106,9 @@ def program_callback():
fpga.pll.pll_outenable_set(False, 0)
fpga.pll.pll_outenable_set(True, 1)
fpga.pll.pll_outenable_set(False, 2)
# Added `pll_frequency` to handle frequencies other than 100MHz.
# Needed this for OTBN ECDSA.
# TODO: Remove these comments after discussion
fpga.pll.pll_outfreq_set(pll_frequency, 1)

# Disable USB clock to reduce noise in power traces.
Expand All @@ -107,18 +119,23 @@ def program_callback():

return fpga

def initialize_scope(self, scope_gain, num_samples, offset):
def initialize_scope(self, scope_gain, num_samples, offset, pll_frequency):
"""Initializes chipwhisperer scope."""
scope = cw.scope()
scope.gain.db = scope_gain
scope.adc.basic_mode = "rising_edge"
if hasattr(scope, '_is_husky') and scope._is_husky:
# We sample using the target clock * 2 (200 MHz).
scope.clock.clkgen_src = 'extclk'
# To fully capture the long OTBN applications,
# we may need to use pll_frequencies other than 100 MHz.
scope.clock.clkgen_freq = pll_frequency
scope.clock.adc_mul = 2
scope.clock.clkgen_freq = 100000000
scope.clock.extclk_monitor_enabled = False
scope.adc.samples = num_samples
scope.clock.clkgen_src = 'extclk'

husky = True
print(f"Husky? = {husky}")
else:
# We sample using the target clock (100 MHz).
scope.clock.adc_mul = 1
Expand All @@ -144,12 +161,47 @@ def initialize_scope(self, scope_gain, num_samples, offset):
assert (scope.clock.adc_locked), "ADC failed to lock"
return scope

def initialize_target(self, programmer, firmware, baudrate, output_len):
def initialize_target(self, programmer, firmware, baudrate, output_len,
pll_frequency):
"""Loads firmware image and initializes test target."""
# To fully capture the long OTBN applications,
# we may need to use pll_frequencies other than 100 MHz.
# As the programming works at 100MHz, we set pll_frequency to 100MHz
if pll_frequency != 100e6:
self.fpga.pll.pll_outfreq_set(100e6, 1)

programmer.bootstrap(firmware)

# To handle the PLL frequencies other than 100e6,after programming is done,
# we switch the pll frequency back to its original value
if pll_frequency != 100e6:
self.fpga.pll.pll_outfreq_set(pll_frequency, 1)

time.sleep(0.5)
target = cw.target(self.scope)
target.output_len = output_len
target.baud = baudrate
# Added `pll_frequency` to handle frequencies other than 100MHz.
# Needed this for OTBN ECDSA.
# TODO: Remove these comments after discussion
target.baud = int(baudrate * pll_frequency / 100e6)
target.flush()

return target

def program_target(self, fw, pll_frequency=100e6):
"""Loads firmware image """
programmer1 = SpiProgrammer(self.fpga)
# To fully capture the long OTBN applications,
# we may need to use pll_frequencies other than 100 MHz.
# As the programming works at 100MHz, we set pll_frequency to 100MHz
if self.scope.clock.clkgen_freq != 100e6:
self.fpga.pll.pll_outfreq_set(100e6, 1)

programmer1.bootstrap(fw)

# To handle the PLL frequencies other than 100e6,after programming is done,
# we switch the pll frequency back to its original value
if self.scope.clock.clkgen_freq != 100e6:
self.fpga.pll.pll_outfreq_set(pll_frequency, 1)

time.sleep(0.5)
2 changes: 1 addition & 1 deletion test/tvla_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(self, args: Args):
def test_help():
tvla = TvlaCmd(Args('--help')).run()
# Assert that a message is printed on stdout or stderr.
assert(len(tvla.stdout()) != 0 or len(tvla.stderr()) != 0)
assert (len(tvla.stdout()) != 0 or len(tvla.stderr()) != 0)


def ttest_significant(ttest_trace) -> bool:
Expand Down

0 comments on commit f51c6ec

Please sign in to comment.