Skip to content

Commit a6fdd2f

Browse files
committed
[test] Add otbn sca
Add host and test scripts for otbn sca. Signed-off-by: Siemen Dhooghe <sdhooghe@google.com>
1 parent f673bd3 commit a6fdd2f

5 files changed

Lines changed: 376 additions & 5 deletions

File tree

communication/sca_otbn_commands.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ def _ujson_otbn_sca_cmd(self):
2424
time.sleep(0.01)
2525
self.target.write(json.dumps("OtbnSca").encode("ascii"))
2626

27-
def init(self) -> list:
28-
""" Initializes the Ibex SCA tests on the target.
27+
def init(self) -> list:
28+
""" Initializes the Otbn SCA tests on the target.
2929
3030
Returns:
3131
Device id
@@ -34,8 +34,8 @@ def init(self) -> list:
3434
The boot measurements
3535
The testOS version
3636
"""
37-
# IbexSca command.
38-
self._ujson_ibex_sca_cmd()
37+
# OtbnSca command.
38+
self._ujson_otbn_sca_cmd()
3939
# Init command.
4040
time.sleep(0.01)
4141
self.target.write(json.dumps("Init").encode("ascii"))

test/penetrationtests/sca/BUILD

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,45 @@ py_binary(
162162
}),
163163
)
164164

165+
py_binary(
166+
name = "sca_otbn_test",
167+
srcs = ["test_scripts/sca_otbn_test.py"],
168+
testonly = True,
169+
deps = [
170+
":sca_otbn_functions",
171+
"//communication:dut",
172+
"//communication:chip",
173+
"//communication:sca_otbn_commands",
174+
"//test/penetrationtests/util:utils",
175+
"@rules_python//python/runfiles",
176+
],
177+
data = [
178+
"@lowrisc_opentitan//sw/host/opentitantool",
179+
] +
180+
select({
181+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/config:env_silicon_owner_gb_rom_ext": [
182+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/firmware:pen_test_sca_silicon_owner_gb_rom_ext",
183+
],
184+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/config:env_fpga_cw310_rom_with_fake_keys": [
185+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/firmware:pen_test_sca_fpga_cw310_rom_with_fake_keys",
186+
],
187+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/config:env_fpga_cw310_sival_rom_ext": [
188+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/firmware:pen_test_sca_fpga_cw310_sival_rom_ext",
189+
],
190+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/config:env_fpga_cw310_test_rom": [
191+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/firmware:pen_test_sca_fpga_cw310_test_rom",
192+
],
193+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/config:env_silicon_owner_a2_rom_ext": [
194+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/firmware:pen_test_sca_silicon_owner_a2_rom_ext",
195+
],
196+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/config:env_silicon_owner_sival_rom_ext": [
197+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/firmware:pen_test_sca_silicon_owner_sival_rom_ext",
198+
],
199+
"//conditions:default": [
200+
"@lowrisc_opentitan//sw/device/tests/penetrationtests/firmware:pen_test_sca_silicon_owner_gb_rom_ext",
201+
],
202+
}),
203+
)
165204

166205
py_library(
167206
name = "sca_ibex_functions",
@@ -212,4 +251,14 @@ py_library(
212251
"//communication:sca_trigger_commands",
213252
requirement("pycryptodome"),
214253
],
254+
)
255+
256+
py_library(
257+
name = "sca_otbn_functions",
258+
srcs = ["host_scripts/sca_otbn_functions.py"],
259+
deps = [
260+
"//communication:dut",
261+
"//communication:chip",
262+
"//communication:sca_otbn_commands",
263+
],
215264
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from communication.sca_otbn_commands import OTOTBN
2+
from communication.chip import *
3+
from communication.dut import DUT
4+
import time
5+
6+
def char_combi_operations_batch(opentitantool, iterations, num_segments, fixed_data1, fixed_data2, print_flag, trigger):
7+
target = DUT()
8+
reset_target(opentitantool)
9+
# Clear the output from the reset
10+
target.dump_all()
11+
12+
otbnsca = OTOTBN(target, "ujson")
13+
# Initialize our chip and catch its output
14+
device_id, owner_page, boot_log, boot_measurements, version = otbnsca.init()
15+
for _ in range(iterations):
16+
otbnsca.start_combi_ops_batch(num_segments, fixed_data1, fixed_data2, print_flag, trigger)
17+
response = target.read_response()
18+
return response
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
from test.penetrationtests.sca.host_scripts.sca_otbn_functions import *
2+
from communication.sca_otbn_commands import OTOTBN
3+
from python.runfiles import Runfiles
4+
from communication.chip import *
5+
from test.penetrationtests.util.utils import *
6+
import os
7+
import json
8+
import random
9+
10+
ignored_keys_set = set([])
11+
12+
def reset_test(opentitantool, target):
13+
reset_target(opentitantool)
14+
while True:
15+
read_line = str(target.readline().decode().strip())
16+
if len(read_line) > 0:
17+
if "firmware_sca.c" in read_line:
18+
return True
19+
else:
20+
return False
21+
22+
23+
def init_test(opentitantool, target):
24+
otbnsca = OTOTBN(target, "ujson")
25+
device_id, owner_page, boot_log, boot_measurements, version = otbnsca.init()
26+
device_id_json = json.loads(device_id)
27+
owner_page_json = json.loads(owner_page)
28+
boot_log_json = json.loads(boot_log)
29+
boot_measurements_json = json.loads(boot_measurements)
30+
31+
expected_device_id_keys = {
32+
"device_id",
33+
"rom_digest",
34+
"icache_en",
35+
"dummy_instr_en",
36+
"clock_jitter_locked",
37+
"clock_jitter_en",
38+
"sram_main_readback_locked",
39+
"sram_main_readback_en",
40+
"sram_ret_readback_locked",
41+
"sram_ret_readback_en"
42+
}
43+
actual_device_id_keys = set(device_id_json.keys())
44+
45+
if not actual_device_id_keys == expected_device_id_keys:
46+
print("device_id keys do not match the expected set.")
47+
print(f"Expected: {expected_device_id_keys}")
48+
print(f"Actual: {actual_device_id_keys}")
49+
return False
50+
51+
expected_owner_page_keys = {
52+
"config_version",
53+
"sram_exec_mode",
54+
"ownership_key_alg",
55+
"update_mode",
56+
"min_security_version_bl0",
57+
"lock_constraint"
58+
}
59+
actual_owner_page_keys = set(owner_page_json.keys())
60+
if not expected_owner_page_keys == actual_owner_page_keys:
61+
print("owner_page keys do not match the expected set.")
62+
print(f"Expected: {expected_owner_page_keys}")
63+
print(f"Actual: {actual_owner_page_keys}")
64+
return False
65+
66+
expected_boot_log_keys = {
67+
"digest",
68+
"identifier",
69+
"scm_revision_low",
70+
"scm_revision_high",
71+
"rom_ext_slot",
72+
"rom_ext_major",
73+
"rom_ext_minor",
74+
"rom_ext_size",
75+
"bl0_slot",
76+
"ownership_state",
77+
"ownership_transfers",
78+
"rom_ext_min_sec_ver",
79+
"bl0_min_sec_ver",
80+
"primary_bl0_slot",
81+
"retention_ram_initialized"
82+
}
83+
actual_boot_log_keys = set(boot_log_json.keys())
84+
if not expected_boot_log_keys == actual_boot_log_keys:
85+
print("boot_log keys do not match the expected set.")
86+
print(f"Expected: {expected_boot_log_keys}")
87+
print(f"Actual: {actual_boot_log_keys}")
88+
return False
89+
90+
expected_boot_measurements_keys = {
91+
"bl0",
92+
"rom_ext"
93+
}
94+
actual_boot_measurements_keys = set(boot_measurements_json.keys())
95+
if not expected_boot_measurements_keys == actual_boot_measurements_keys:
96+
print("boot_measurements keys do not match the expected set.")
97+
print(f"Expected: {expected_boot_measurements_keys}")
98+
print(f"Actual: {actual_boot_measurements_keys}")
99+
return False
100+
101+
if "PENTEST" not in version:
102+
print("Did not receive a PENTEST version.")
103+
print(f"Actual: {version}")
104+
return False
105+
106+
return True
107+
108+
def char_combi_operations_batch_test(opentitantool_path, iterations, num_segments):
109+
trigger = 0
110+
fixed_data1 = 0
111+
fixed_data2 = 0
112+
print_flag = True
113+
actual_result = char_combi_operations_batch(opentitantool_path, iterations, num_segments, fixed_data1, fixed_data2, print_flag, trigger)
114+
try:
115+
actual_result_json = json.loads(actual_result)
116+
except:
117+
print("char_combi_operations_batch gave an unexpected result")
118+
print(f"Input fixed_data1 {fixed_data1} and fixed_data2 {fixed_data2}")
119+
print(f"Actual: {actual_result}")
120+
return False
121+
122+
# Calculate the expected result
123+
fixed_data_array1 = [fixed_data1 for _ in range(8)]
124+
fixed_data_array2 = [fixed_data2 for _ in range(8)]
125+
add = int_to_array((array_to_int(fixed_data_array1)+array_to_int(fixed_data_array2))%(1<<256))
126+
sub = int_to_array((array_to_int(fixed_data_array1)-array_to_int(fixed_data_array2))%(1<<256))
127+
xor = int_to_array((array_to_int(fixed_data_array1)^array_to_int(fixed_data_array2))%(1<<256))
128+
shift = int_to_array((array_to_int(fixed_data_array1)<<1)%(1<<256))
129+
fixed_data_array1 = [fixed_data1, fixed_data1, 0, 0, 0, 0, 0, 0]
130+
fixed_data_array2 = [fixed_data2, fixed_data2, 0, 0, 0, 0, 0, 0]
131+
mult = int_to_array((array_to_int(fixed_data_array1)*array_to_int(fixed_data_array2))%(1<<256))
132+
FG = 0
133+
if fixed_data1 == fixed_data2:
134+
FG += 8
135+
if fixed_data1 < fixed_data2:
136+
FG += 1
137+
if sub[0] & 0x1:
138+
FG += 4
139+
if (array_to_int(sub) >> 255) & 0x1:
140+
FG += 2
141+
142+
expected_result_json = {
143+
"result1": [fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1],
144+
"result2": [fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1],
145+
"result3": add,
146+
"result4": sub,
147+
"result5": xor,
148+
"result6": shift,
149+
"result7": mult,
150+
"result8": FG,
151+
}
152+
if not compare_json_data(actual_result_json, expected_result_json, ignored_keys_set):
153+
print("char_combi_operations_batch_fvsr failed")
154+
print(f"Input fixed_data1 {fixed_data1} and fixed_data2 {fixed_data2}")
155+
print(f"Expected: {expected_result_json}")
156+
print(f"Actual: {actual_result_json}")
157+
print("")
158+
return False
159+
160+
fixed_data1 = 1
161+
fixed_data2 = 1
162+
print_flag = True
163+
actual_result = char_combi_operations_batch(opentitantool_path, iterations, num_segments, fixed_data1, fixed_data2, print_flag, trigger)
164+
try:
165+
actual_result_json = json.loads(actual_result)
166+
except:
167+
print("char_combi_operations_batch gave an unexpected result")
168+
print(f"Input fixed_data1 {fixed_data1} and fixed_data2 {fixed_data2}")
169+
print(f"Actual: {actual_result}")
170+
return False
171+
172+
# Calculate the expected result
173+
fixed_data_array1 = [fixed_data1 for _ in range(8)]
174+
fixed_data_array2 = [fixed_data2 for _ in range(8)]
175+
add = int_to_array((array_to_int(fixed_data_array1)+array_to_int(fixed_data_array2))%(1<<256))
176+
sub = int_to_array((array_to_int(fixed_data_array1)-array_to_int(fixed_data_array2))%(1<<256))
177+
xor = int_to_array((array_to_int(fixed_data_array1)^array_to_int(fixed_data_array2))%(1<<256))
178+
shift = int_to_array((array_to_int(fixed_data_array1)<<1)%(1<<256))
179+
fixed_data_array1 = [fixed_data1, fixed_data1, 0, 0, 0, 0, 0, 0]
180+
fixed_data_array2 = [fixed_data2, fixed_data2, 0, 0, 0, 0, 0, 0]
181+
mult = int_to_array((array_to_int(fixed_data_array1)*array_to_int(fixed_data_array2))%(1<<256))
182+
FG = 0
183+
if fixed_data1 == fixed_data2:
184+
FG += 8
185+
if fixed_data1 < fixed_data2:
186+
FG += 1
187+
if sub[0] & 0x1:
188+
FG += 4
189+
if (array_to_int(sub) >> 255) & 0x1:
190+
FG += 2
191+
192+
expected_result_json = {
193+
"result1": [fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1],
194+
"result2": [fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1],
195+
"result3": add,
196+
"result4": sub,
197+
"result5": xor,
198+
"result6": shift,
199+
"result7": mult,
200+
"result8": FG,
201+
}
202+
if not compare_json_data(actual_result_json, expected_result_json, ignored_keys_set):
203+
print("char_combi_operations_batch_fvsr failed")
204+
print(f"Input fixed_data1 {fixed_data1} and fixed_data2 {fixed_data2}")
205+
print(f"Expected: {expected_result_json}")
206+
print(f"Actual: {actual_result_json}")
207+
print("")
208+
return False
209+
210+
fixed_data1 = random.getrandbits(32)
211+
fixed_data2 = random.getrandbits(32)
212+
print_flag = True
213+
actual_result = char_combi_operations_batch(opentitantool_path, iterations, num_segments, fixed_data1, fixed_data2, print_flag, trigger)
214+
try:
215+
actual_result_json = json.loads(actual_result)
216+
except:
217+
print("char_combi_operations_batch gave an unexpected result")
218+
print(f"Input fixed_data1 {fixed_data1} and fixed_data2 {fixed_data2}")
219+
print(f"Actual: {actual_result}")
220+
return False
221+
222+
# Calculate the expected result
223+
fixed_data_array1 = [fixed_data1 for _ in range(8)]
224+
fixed_data_array2 = [fixed_data2 for _ in range(8)]
225+
add = int_to_array((array_to_int(fixed_data_array1)+array_to_int(fixed_data_array2))%(1<<256))
226+
sub = int_to_array((array_to_int(fixed_data_array1)-array_to_int(fixed_data_array2))%(1<<256))
227+
xor = int_to_array((array_to_int(fixed_data_array1)^array_to_int(fixed_data_array2))%(1<<256))
228+
shift = int_to_array((array_to_int(fixed_data_array1)<<1)%(1<<256))
229+
fixed_data_array1 = [fixed_data1, fixed_data1, 0, 0, 0, 0, 0, 0]
230+
fixed_data_array2 = [fixed_data2, fixed_data2, 0, 0, 0, 0, 0, 0]
231+
mult = int_to_array((array_to_int(fixed_data_array1)*array_to_int(fixed_data_array2))%(1<<256))
232+
FG = 0
233+
if fixed_data1 == fixed_data2:
234+
FG += 8
235+
if fixed_data1 < fixed_data2:
236+
FG += 1
237+
if sub[0] & 0x1:
238+
FG += 4
239+
if (array_to_int(sub) >> 255) & 0x1:
240+
FG += 2
241+
242+
expected_result_json = {
243+
"result1": [fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1],
244+
"result2": [fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1, fixed_data1],
245+
"result3": add,
246+
"result4": sub,
247+
"result5": xor,
248+
"result6": shift,
249+
"result7": mult,
250+
"result8": FG,
251+
}
252+
if not compare_json_data(actual_result_json, expected_result_json, ignored_keys_set):
253+
print("char_combi_operations_batch_fvsr failed")
254+
print(f"Input fixed_data1 {fixed_data1} and fixed_data2 {fixed_data2}")
255+
print(f"Expected: {expected_result_json}")
256+
print(f"Actual: {actual_result_json}")
257+
print("")
258+
return False
259+
260+
return True
261+
262+
def main():
263+
r = Runfiles.Create()
264+
opentitantool_path = r.Rlocation("lowrisc_opentitan/sw/host/opentitantool/opentitantool")
265+
266+
firmware_target_name = os.environ.get("SELECTED_FIRMWARE_TARGET", "pen_test_sca_silicon_owner_gb_rom_ext")
267+
firmware_path = r.Rlocation(f"lowrisc_opentitan/sw/device/tests/penetrationtests/firmware/{firmware_target_name}.img")
268+
269+
target = DUT()
270+
271+
flash_target(opentitantool_path, firmware_path)
272+
target.dump_all()
273+
274+
if not reset_test(opentitantool_path, target):
275+
print("Reset test failure")
276+
return False
277+
278+
if not init_test(opentitantool_path, target):
279+
print("Init test failure")
280+
return False
281+
282+
iterations = 10
283+
num_segments_list = [1, 2, 5, 10, 12]
284+
285+
for num_segments in num_segments_list:
286+
char_combi_operations_batch_test(opentitantool_path, iterations, num_segments)
287+
288+
print("Testing finished")
289+
return True
290+
291+
if __name__ == "__main__":
292+
main()

0 commit comments

Comments
 (0)