Skip to content

Commit 7748410

Browse files
committed
[device] CW340 support
This PR adds experimental CW340 support with a minimal running capture script. Signed-off-by: Pascal Nasahl <[email protected]>
1 parent 5abf4c6 commit 7748410

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

cw/simple_capture_aes_sca_cw340.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#!/usr/bin/env python3
2+
# Copyright lowRISC contributors.
3+
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
4+
# SPDX-License-Identifier: Apache-2.0
5+
import binascii
6+
import random
7+
import signal
8+
import sys
9+
import time
10+
from datetime import datetime
11+
from functools import partial
12+
from pathlib import Path
13+
14+
import chipwhisperer as cw
15+
import numpy as np
16+
import yaml
17+
from Crypto.Cipher import AES
18+
from tqdm import tqdm
19+
20+
from util import device, plot, trace_util
21+
22+
23+
def abort_handler_during_loop(project, sig, frame):
24+
# Handler for ctrl-c keyboard interrupts
25+
# TODO: Has to be modified according to database (i.e. CW project atm) used
26+
if project is not None:
27+
print("\nHandling keyboard interrupt")
28+
project.close(save=True)
29+
sys.exit(0)
30+
31+
32+
if __name__ == '__main__':
33+
# Load configuration from file
34+
with open('simple_capture_aes_sca_cw340.yaml') as f:
35+
cfg = yaml.load(f, Loader=yaml.FullLoader)
36+
37+
USE_HUSKY = True
38+
39+
# Create ChipWhisperer project for storage of traces and metadata
40+
project = cw.create_project(cfg["capture"]["project_name"], overwrite=True)
41+
42+
# Create OpenTitan encapsulating ChipWhisperer Husky and FPGA
43+
# NOTE: A clean separation of the two seems infeasible since
44+
# scope needs FPGA (PLL?) to be configured and target constructor needs scope as input.
45+
cwfpgahusky = device.OpenTitan(cfg["cwfpgahusky"]["fpga_bitstream"],
46+
cfg["cwfpgahusky"]["force_program_bitstream"],
47+
cfg["cwfpgahusky"]["fw_bin"],
48+
cfg["cwfpgahusky"]["pll_frequency"],
49+
cfg["cwfpgahusky"]["baudrate"],
50+
cfg["cwfpgahusky"]["scope_gain"],
51+
cfg["cwfpgahusky"]["num_samples"],
52+
cfg["cwfpgahusky"]["offset"],
53+
cfg["cwfpgahusky"]["output_len_bytes"])
54+
55+
56+
# Register ctrl-c handler to store traces on abort
57+
signal.signal(signal.SIGINT, partial(abort_handler_during_loop, project))
58+
59+
# Preparation of Key and plaintext generation
60+
# Generate key at random based on test_random_seed (not used atm)
61+
random.seed(cfg["test"]["test_random_seed"])
62+
key = bytearray(cfg["test"]["key_len_bytes"])
63+
for i in range(0, cfg["test"]["key_len_bytes"]):
64+
key[i] = random.randint(0, 255)
65+
# Load initial key and text values from cfg
66+
key = bytearray(cfg["test"]["key"])
67+
print(f'Using key: {binascii.b2a_hex(bytes(key))}')
68+
text = bytearray(cfg["test"]["text"])
69+
# Prepare generation of new texts/keys by encryption using key_for_generation
70+
key_for_gen = bytearray(cfg["test"]["key_for_gen"])
71+
cipher_gen = AES.new(bytes(key_for_gen), AES.MODE_ECB)
72+
73+
# Set key
74+
cwfpgahusky.target.simpleserial_write("k", key)
75+
76+
# Cipher to compute expected responses
77+
cipher = AES.new(bytes(key), AES.MODE_ECB)
78+
79+
# # Main loop for measurements with progress bar
80+
# for _ in tqdm(range(cfg["capture"]["num_traces"]), desc='Capturing', ncols=80):
81+
82+
# # TODO: Useful code line for batch capture
83+
# # cwfpgahusky..simpleserial_write("s", capture_cfg["batch_prng_seed"].to_bytes(4, "little"))
84+
85+
# # Note: Capture performance tested Oct. 2023:
86+
# # Using husky with 1200 samples per trace leads to 48 it/s
87+
# # Using Waverunner with 1200 - 50000 samples per trace leads to 27 it/s
88+
# # Increases to 31 it/s when 'Performance' set to 'Analysis' in Utilies->Preferences
89+
# # Transfer over UART only slows down if .e.g transfering key 5 additional times
90+
91+
# if USE_HUSKY:
92+
# # Arm Husky scope
93+
# cwfpgahusky.scope.arm()
94+
95+
# # Generate new text for this iteration
96+
# text = bytearray(cipher_gen.encrypt(text))
97+
# # Load text and trigger execution
98+
# cwfpgahusky.target.simpleserial_write('p', text)
99+
100+
# if USE_HUSKY:
101+
# # Capture Husky trace
102+
# ret = cwfpgahusky.scope.capture(poll_done=False)
103+
# i = 0
104+
# while not cwfpgahusky.target.is_done():
105+
# i += 1
106+
# time.sleep(0.05)
107+
# if i > 100:
108+
# print("Warning: Target did not finish operation")
109+
# if ret:
110+
# print("Warning: Timeout happened during capture")
111+
112+
# # Get Husky trace
113+
# wave = cwfpgahusky.scope.get_last_trace(as_int=True)
114+
115+
# if USE_WAVERUNNER:
116+
# # Capture and get Waverunner trace
117+
# waves = waverunner.capture_and_transfer_waves()
118+
# assert waves.shape[0] == cfg["waverunner"]["num_segments"]
119+
# # For single capture, 1st dim contains wave data
120+
# wave = waves[0, :]
121+
# # Put into uint8 range
122+
# wave = wave + 128
123+
124+
# # Get response from device and verify
125+
# response = cwfpgahusky.target.simpleserial_read('r',
126+
# cwfpgahusky.target.output_len, ack=False)
127+
# if binascii.b2a_hex(response) != binascii.b2a_hex(cipher.encrypt(bytes(text))):
128+
# raise RuntimeError(f'Bad ciphertext: {response} != {cipher.encrypt(bytes(text))}.')
129+
130+
# # TODO: Useful code line for batch capture
131+
# # waves = scope.capture_and_transfer_waves()
132+
133+
# # Sanity check retrieved data (wave) and create CW Trace
134+
# if len(wave) >= 1:
135+
# trace = cw.Trace(wave, text, response, key)
136+
# else:
137+
# raise RuntimeError('Capture failed.')
138+
139+
# if USE_HUSKY:
140+
# # Check if ADC range has been exceeded for Husky.
141+
# # Not done for WaveRunner because clipping can be inspected on screen.
142+
# trace_util.check_range(trace.wave, cwfpgahusky.scope.adc.bits_per_sample)
143+
144+
# # Append CW trace to CW project storage
145+
# if USE_HUSKY:
146+
# project.traces.append(trace, dtype=np.uint16)
147+
# if USE_WAVERUNNER:
148+
# # Also use uint16 as dtype so that tvla processing works
149+
# project.traces.append(trace, dtype=np.uint16)
150+
151+
# # Save metadata and entire configuration cfg to project file
152+
# project.settingsDict['datetime'] = datetime.now().strftime("%m/%d/%Y, %H:%M:%S")
153+
# project.settingsDict['cfg'] = cfg
154+
# sample_rate = int(round(cwfpgahusky.scope.clock.adc_freq, -6))
155+
# project.settingsDict['sample_rate'] = sample_rate
156+
# project.save()
157+
158+
# # Create and show test plot
159+
# if cfg["capture"]["show_plot"]:
160+
# plot.save_plot_to_file(project.waves, None, cfg["capture"]["plot_traces"],
161+
# cfg["capture"]["trace_image_filename"], add_mean_stddev=True)
162+
# print(f'Created plot with {cfg["capture"]["plot_traces"]} traces: '
163+
# f'{Path(cfg["capture"]["trace_image_filename"]).resolve()}')
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
cwfpgahusky:
2+
baudrate: 115200
3+
output_len_bytes: 16
4+
pll_frequency: 100000000
5+
adc_mul: 2
6+
num_segments: 1
7+
num_samples: 1200
8+
offset: -40
9+
scope_gain: 31.5
10+
capture:
11+
scope_select: husky
12+
num_traces: 1000
13+
project_name: "projects/simple_capture_aes_sca"
14+
show_plot: True
15+
plot_traces: 100
16+
trace_image_filename: "projects/simple_capture_aes_sca_sample_traces.html"
17+
test:
18+
key_len_bytes: 16
19+
test_random_seed: 0
20+
key: [0x81, 0x1E, 0x37, 0x31, 0xB0, 0x12, 0x0A, 0x78, 0x42, 0x78, 0x1E, 0x22, 0xB2, 0x5C, 0xDD, 0xF9]
21+
text: [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA]
22+
key_for_gen: [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF1, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xE0, 0xF0]

0 commit comments

Comments
 (0)