Skip to content

Commit

Permalink
[device] CW340 support
Browse files Browse the repository at this point in the history
This PR adds experimental CW340 support with a minimal running
capture script.

Signed-off-by: Pascal Nasahl <[email protected]>
  • Loading branch information
nasahlpa committed Oct 18, 2023
1 parent 5abf4c6 commit 7748410
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
163 changes: 163 additions & 0 deletions cw/simple_capture_aes_sca_cw340.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import binascii
import random
import signal
import sys
import time
from datetime import datetime
from functools import partial
from pathlib import Path

import chipwhisperer as cw
import numpy as np
import yaml
from Crypto.Cipher import AES
from tqdm import tqdm

from util import device, plot, trace_util


def abort_handler_during_loop(project, sig, frame):
# Handler for ctrl-c keyboard interrupts
# TODO: Has to be modified according to database (i.e. CW project atm) used
if project is not None:
print("\nHandling keyboard interrupt")
project.close(save=True)
sys.exit(0)


if __name__ == '__main__':
# Load configuration from file
with open('simple_capture_aes_sca_cw340.yaml') as f:
cfg = yaml.load(f, Loader=yaml.FullLoader)

USE_HUSKY = True

# Create ChipWhisperer project for storage of traces and metadata
project = cw.create_project(cfg["capture"]["project_name"], overwrite=True)

# Create OpenTitan encapsulating ChipWhisperer Husky and FPGA
# NOTE: A clean separation of the two seems infeasible since
# scope needs FPGA (PLL?) to be configured and target constructor needs scope as input.
cwfpgahusky = device.OpenTitan(cfg["cwfpgahusky"]["fpga_bitstream"],
cfg["cwfpgahusky"]["force_program_bitstream"],
cfg["cwfpgahusky"]["fw_bin"],
cfg["cwfpgahusky"]["pll_frequency"],
cfg["cwfpgahusky"]["baudrate"],
cfg["cwfpgahusky"]["scope_gain"],
cfg["cwfpgahusky"]["num_samples"],
cfg["cwfpgahusky"]["offset"],
cfg["cwfpgahusky"]["output_len_bytes"])


# Register ctrl-c handler to store traces on abort
signal.signal(signal.SIGINT, partial(abort_handler_during_loop, project))

# Preparation of Key and plaintext generation
# Generate key at random based on test_random_seed (not used atm)
random.seed(cfg["test"]["test_random_seed"])
key = bytearray(cfg["test"]["key_len_bytes"])
for i in range(0, cfg["test"]["key_len_bytes"]):
key[i] = random.randint(0, 255)
# Load initial key and text values from cfg
key = bytearray(cfg["test"]["key"])
print(f'Using key: {binascii.b2a_hex(bytes(key))}')
text = bytearray(cfg["test"]["text"])
# Prepare generation of new texts/keys by encryption using key_for_generation
key_for_gen = bytearray(cfg["test"]["key_for_gen"])
cipher_gen = AES.new(bytes(key_for_gen), AES.MODE_ECB)

# Set key
cwfpgahusky.target.simpleserial_write("k", key)

# Cipher to compute expected responses
cipher = AES.new(bytes(key), AES.MODE_ECB)

# # Main loop for measurements with progress bar
# for _ in tqdm(range(cfg["capture"]["num_traces"]), desc='Capturing', ncols=80):

# # TODO: Useful code line for batch capture
# # cwfpgahusky..simpleserial_write("s", capture_cfg["batch_prng_seed"].to_bytes(4, "little"))

# # Note: Capture performance tested Oct. 2023:
# # Using husky with 1200 samples per trace leads to 48 it/s
# # Using Waverunner with 1200 - 50000 samples per trace leads to 27 it/s
# # Increases to 31 it/s when 'Performance' set to 'Analysis' in Utilies->Preferences
# # Transfer over UART only slows down if .e.g transfering key 5 additional times

# if USE_HUSKY:
# # Arm Husky scope
# cwfpgahusky.scope.arm()

# # Generate new text for this iteration
# text = bytearray(cipher_gen.encrypt(text))
# # Load text and trigger execution
# cwfpgahusky.target.simpleserial_write('p', text)

# if USE_HUSKY:
# # Capture Husky trace
# ret = cwfpgahusky.scope.capture(poll_done=False)
# i = 0
# while not cwfpgahusky.target.is_done():
# i += 1
# time.sleep(0.05)
# if i > 100:
# print("Warning: Target did not finish operation")
# if ret:
# print("Warning: Timeout happened during capture")

# # Get Husky trace
# wave = cwfpgahusky.scope.get_last_trace(as_int=True)

# if USE_WAVERUNNER:
# # Capture and get Waverunner trace
# waves = waverunner.capture_and_transfer_waves()
# assert waves.shape[0] == cfg["waverunner"]["num_segments"]
# # For single capture, 1st dim contains wave data
# wave = waves[0, :]
# # Put into uint8 range
# wave = wave + 128

# # Get response from device and verify
# response = cwfpgahusky.target.simpleserial_read('r',
# cwfpgahusky.target.output_len, ack=False)
# if binascii.b2a_hex(response) != binascii.b2a_hex(cipher.encrypt(bytes(text))):
# raise RuntimeError(f'Bad ciphertext: {response} != {cipher.encrypt(bytes(text))}.')

# # TODO: Useful code line for batch capture
# # waves = scope.capture_and_transfer_waves()

# # Sanity check retrieved data (wave) and create CW Trace
# if len(wave) >= 1:
# trace = cw.Trace(wave, text, response, key)
# else:
# raise RuntimeError('Capture failed.')

# if USE_HUSKY:
# # Check if ADC range has been exceeded for Husky.
# # Not done for WaveRunner because clipping can be inspected on screen.
# trace_util.check_range(trace.wave, cwfpgahusky.scope.adc.bits_per_sample)

# # Append CW trace to CW project storage
# if USE_HUSKY:
# project.traces.append(trace, dtype=np.uint16)
# if USE_WAVERUNNER:
# # Also use uint16 as dtype so that tvla processing works
# project.traces.append(trace, dtype=np.uint16)

# # Save metadata and entire configuration cfg to project file
# project.settingsDict['datetime'] = datetime.now().strftime("%m/%d/%Y, %H:%M:%S")
# project.settingsDict['cfg'] = cfg
# sample_rate = int(round(cwfpgahusky.scope.clock.adc_freq, -6))
# project.settingsDict['sample_rate'] = sample_rate
# project.save()

# # Create and show test plot
# if cfg["capture"]["show_plot"]:
# plot.save_plot_to_file(project.waves, None, cfg["capture"]["plot_traces"],
# cfg["capture"]["trace_image_filename"], add_mean_stddev=True)
# print(f'Created plot with {cfg["capture"]["plot_traces"]} traces: '
# f'{Path(cfg["capture"]["trace_image_filename"]).resolve()}')
22 changes: 22 additions & 0 deletions cw/simple_capture_aes_sca_cw340.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
cwfpgahusky:
baudrate: 115200
output_len_bytes: 16
pll_frequency: 100000000
adc_mul: 2
num_segments: 1
num_samples: 1200
offset: -40
scope_gain: 31.5
capture:
scope_select: husky
num_traces: 1000
project_name: "projects/simple_capture_aes_sca"
show_plot: True
plot_traces: 100
trace_image_filename: "projects/simple_capture_aes_sca_sample_traces.html"
test:
key_len_bytes: 16
test_random_seed: 0
key: [0x81, 0x1E, 0x37, 0x31, 0xB0, 0x12, 0x0A, 0x78, 0x42, 0x78, 0x1E, 0x22, 0xB2, 0x5C, 0xDD, 0xF9]
text: [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA]
key_for_gen: [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF1, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xE0, 0xF0]

0 comments on commit 7748410

Please sign in to comment.