Skip to content

Commit

Permalink
[tvla/otbn] add fixed-vs-random tvla for otbn
Browse files Browse the repository at this point in the history
This is a initial work to add fixed-vs.-random tvla tests for otbn.

Signed-off-by: Michael Tempelmeier <[email protected]>
  • Loading branch information
m-temp committed Feb 27, 2023
1 parent cd8c252 commit bab88b4
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 25 deletions.
48 changes: 32 additions & 16 deletions cw/capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,7 @@ def capture_otbn_vertical(ot, ktp, fw_bin, pll_frequency, capture_cfg):
if ot.scope._is_husky:
ot.scope.adc.stream_mode = False
ot.scope.adc.bits_per_sample = 12
ot.scope.adc.samples = fifo_size
ot.scope.adc.samples = capture_cfg["num_samples"]
else:
# TODO: Add cw-lite support
raise RuntimeError('Only CW-Husky is supported now')
Expand Down Expand Up @@ -1060,15 +1060,16 @@ def capture_otbn_vertical(ot, ktp, fw_bin, pll_frequency, capture_cfg):

# Seed the RNG and generate a random fixed seed for all traces of the
# keygen operation.
seed = ktp.next_key()
print(f'seed = {seed.hex()}')
if len(seed) != seed_bytes:
raise ValueError(f'Seed length is {len(seed)}, expected {seed_bytes}')
seed_fixed = ktp.next_key()
print(f'fixed seed = {seed_fixed.hex()}')
if len(seed_fixed) != seed_bytes:
raise ValueError(f'Fixed seed length is {len(seed_fixed)}, expected {seed_bytes}')

# Expected key is `seed mod n`, where n is the order of the curve and
# `seed` is interpreted as little-endian.
expected_key = int.from_bytes(seed, byteorder='little') % curve_order_n
expected_fixed_key = int.from_bytes(seed_fixed, byteorder='little') % curve_order_n

sample_fixed = 1
# Loop to collect each power trace
for _ in tqdm(range(capture_cfg["num_traces"]), desc='Capturing',
ncols=80):
Expand All @@ -1081,9 +1082,22 @@ def capture_otbn_vertical(ot, ktp, fw_bin, pll_frequency, capture_cfg):
# Generate a new random mask for each trace.
mask = ktp.next_text()
tqdm.write(f'mask = {mask.hex()}')
tqdm.write("Starting new trace....")

if sample_fixed:
# Use the fixed seed.
seed_used = seed_fixed
expected_key = expected_fixed_key
else:
# Use a random seed.
seed_used = ktp.next_key()
expected_key = int.from_bytes(seed_used, byteorder='little') % curve_order_n

# Decide for next round if we use the fixed or a random seed.
sample_fixed = random.randint(0, 1)

# Set the seed.
ot.target.simpleserial_write('x', seed)
ot.target.simpleserial_write('x', seed_used)
tqdm.write(f'seed = {seed_used.hex()}')

# Check for errors.
err = ot.target.read()
Expand All @@ -1098,10 +1112,6 @@ def capture_otbn_vertical(ot, ktp, fw_bin, pll_frequency, capture_cfg):

# Wait until operation is done.
ret = ot.scope.capture(poll_done=True)
# TODO: change this
# If getting inconsistent results (e.g. variable number of cycles),
# adding a sufficient sleep below here appears to fix things
# time.sleep(1) # we cannot afford 1 second per run.
if ret:
raise RuntimeError('Timeout during capture')

Expand All @@ -1114,21 +1124,27 @@ def capture_otbn_vertical(ot, ktp, fw_bin, pll_frequency, capture_cfg):
# expectations.
share0 = ot.target.simpleserial_read("r", seed_bytes, ack=False)
share1 = ot.target.simpleserial_read("r", seed_bytes, ack=False)
if share0 is None or share1 is None:
raise RuntimeError('Did not receive expected output.')
if share0 is None:
raise RuntimeError('Random share0 is none')
if share1 is None:
raise RuntimeError('Random share1 is none')

d0 = int.from_bytes(share0, byteorder='little')
d1 = int.from_bytes(share1, byteorder='little')
actual_key = (d0 + d1) % curve_order_n

tqdm.write(f'share0 = {share0.hex()}')
tqdm.write(f'share1 = {share1.hex()}')

if actual_key != expected_key:
raise RuntimeError('Bad generated key:\n'
f'Expected: {hex(expected_key)}\n'
f'Actual: {hex(actual_key)}')

# Create a chipwhisperer trace object and save it to the project
# Args/fields of Trace object: waves, textin, textout, key
textout = share0 + share1
trace = Trace(waves, mask, textout, seed)
textout = share0 + share1 # concatenate bytearrays
trace = Trace(waves, mask, textout, seed_used)
check_range(waves, ot.scope.adc.bits_per_sample)
project.traces.append(trace, dtype=np.uint16)

Expand Down
4 changes: 2 additions & 2 deletions cw/capture_otbn_vertical.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ capture:
output_len_bytes: 40
# Samples per trace
# Fifo Size is 131070 = max num samples to avoid multiple runs per trace
num_samples: 120000 # 6000 cycles*20 sampling points
num_samples: 8000 # 6000 cycles*20 sampling points
# 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: 20
num_traces: 10000
project_name: projects/opentitan_otbn_vertical_keygen
waverunner_ip: 192.168.1.228
# As OTBN operations are long, we may need to overwrite the following
Expand Down
27 changes: 20 additions & 7 deletions cw/tvla.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,15 +336,18 @@ def run_tvla(ctx: typer.Context):
level=log.INFO,
force=True,)

if cfg["mode"] != "kmac" and cfg["mode"] != "aes" and cfg["mode"] != "sha3":
if (cfg["mode"] != "kmac" and cfg["mode"] != "aes" and cfg["mode"] != "sha3" and
cfg["mode"] != "otbn"):
log.info("Unsupported mode:" + cfg["mode"] + ", falling back to \"aes\"")

if cfg["mode"] == "kmac" or cfg["mode"] == "sha3" or cfg["general_test"] is True:
if (cfg["mode"] == "kmac" or cfg["mode"] == "otbn" or cfg["mode"] == "sha3" or
cfg["general_test"] is True):
general_test = True
else:
general_test = False

if cfg["mode"] == "kmac" or cfg["mode"] == "sha3" or general_test is True:
if (cfg["mode"] == "kmac" or cfg["mode"] == "otbn" or cfg["mode"] == "sha3" or
general_test is True):
# We don't care about the round select in this mode. Set it to 0 for code compatibility.
rnd_list = [0]
elif cfg["round_select"] is None:
Expand All @@ -355,7 +358,8 @@ def run_tvla(ctx: typer.Context):

num_rnds = len(rnd_list)

if cfg["mode"] == "kmac" or cfg["mode"] == "sha3" or general_test is True:
if (cfg["mode"] == "kmac" or cfg["mode"] == "otbn" or cfg["mode"] == "sha3" or
general_test is True):
# We don't care about the byte select in this mode. Set it to 0 for code compatibility.
byte_list = [0]
elif cfg["byte_select"] is None:
Expand All @@ -366,6 +370,12 @@ def run_tvla(ctx: typer.Context):

num_bytes = len(byte_list)

if cfg["mode"] == "otbn":
if "key_len_bytes" not in cfg:
raise RuntimeError('key_len_bytes must be set for otbn mode!')
else:
key_len_bytes = cfg["key_len_bytes"]

num_steps = int(cfg["number_of_steps"])
assert num_steps >= 1

Expand Down Expand Up @@ -578,7 +588,10 @@ def run_tvla(ctx: typer.Context):
if cfg["leakage_file"] is None:
# Create local, dense copies of keys and plaintexts. This allows the leakage
# computation to be parallelized.
keys = np.empty((num_traces_orig, 16), dtype=np.uint8)
if cfg["mode"] == "otbn":
keys = np.empty((num_traces_orig, key_len_bytes), dtype=np.uint8)
else:
keys = np.empty((num_traces_orig, 16), dtype=np.uint8)

if general_test is False:
keys[:] = project.keys[trace_start:trace_end + 1]
Expand Down Expand Up @@ -992,8 +1005,8 @@ def run_tvla(ctx: typer.Context):
help_general_test = inspect.cleandoc("""Perform general fixed-vs-random TVLA without leakage
model. Odd traces are grouped in the fixed set while even traces are grouped in the random set.
Default: """ + str(default_general_test))
help_mode = inspect.cleandoc("""Select mode: can be either "aes", "kmac", or "sha3". Default:
""" + str(default_mode))
help_mode = inspect.cleandoc("""Select mode: can be either "aes", "kmac", "sha3", or "otbn".
Default: """ + str(default_mode))
help_update_cfg_file = inspect.cleandoc("""Update existing configuration file or create if there
isn't any configuration file. Default: """ + str(default_update_cfg_file))

Expand Down
16 changes: 16 additions & 0 deletions cw/tvla_cfg_otbn.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
project_file: projects/opentitan_otbn_vertical_keygen.cwp
trace_file: null
trace_start: null
trace_end: null
leakage_file: null
save_to_disk: null
round_select: null
byte_select: null
input_file: null
output_file: null
number_of_steps: 1
ttest_step_file: null
plot_figures: true
general_test: true
mode: otbn
key_len_bytes: 40

0 comments on commit bab88b4

Please sign in to comment.