Skip to content

Commit a0f6f56

Browse files
siemen11nasahlpa
authored andcommitted
[FiSim] Add cmac
Add an instruction skip routine for cmac. Signed-off-by: Siemen Dhooghe <sdhooghe@google.com>
1 parent ca6fc9f commit a0f6f56

1 file changed

Lines changed: 231 additions & 0 deletions

File tree

sw/host/penetrationtests/python/fi/gdb_testing/fi_sym_cryptolib_python_gdb_test.py

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,237 @@ def test_gcm(self):
967967
self.assertEqual(successful_faults, 0)
968968
self.assertEqual(started, True)
969969

970+
def test_cmac(self):
971+
print("Starting the cmac test")
972+
data_len = 32
973+
# We prepare two data inputs and check for collisions between them
974+
data1 = [i for i in range(data_len)]
975+
data2 = [data_len - i for i in range(data_len)]
976+
data = [data1, data2]
977+
key_len = 16
978+
key = [i for i in range(key_len)]
979+
iv = [i for i in range(16)]
980+
cfg = 0
981+
trigger = 0
982+
983+
# Directory for the trace log files
984+
pc_trace_file = os.path.join(log_dir, "cmac_pc_trace.log")
985+
# Directory for the log of the campaign
986+
campaign_file = os.path.join(log_dir, "cmac_test_campaign.log")
987+
988+
successful_faults = 0
989+
total_attacks = 0
990+
991+
gdb = None
992+
started = False
993+
with open(campaign_file, "w") as campaign:
994+
print(f"Switching terminal output to {campaign_file}", flush=True)
995+
sys.stdout = campaign
996+
try:
997+
# Program the bitstream, flash the target, and set up OpenOCD
998+
target.initialize_target()
999+
1000+
# Initialize the testOS
1001+
trigger_testos_init()
1002+
1003+
# Connect to GDB
1004+
gdb = GDBController(
1005+
gdb_path=GDB_PATH, gdb_port=GDB_PORT, elf_file=elf_path
1006+
)
1007+
1008+
# We provide the name of the unique marker in the pentest framework
1009+
function_name = "PENTEST_MARKER_CMAC"
1010+
# Gives back an array of hits where the function is called
1011+
trace_address = parser.get_marker_addresses(function_name)
1012+
print(
1013+
"Start and stop addresses of ", function_name, ": ", trace_address
1014+
)
1015+
1016+
crash_observation_address = parser.get_function_start_address(
1017+
"ottf_exception_handler"
1018+
)
1019+
1020+
# Start the tracing
1021+
# We set a short timeout to detect whether GDB has connected properly
1022+
# and a long timeout for the entire tracing
1023+
initial_timeout = 10
1024+
total_timeout = 60 * 60 * 5
1025+
1026+
gdb.setup_pc_trace(pc_trace_file, trace_address[0], trace_address[1])
1027+
gdb.send_command("c", check_response=False)
1028+
1029+
# Trigger the cmac from the testOS (we do not read its output)
1030+
symfi.handle_cmac(data[0], data_len, key, key_len, iv, cfg, trigger)
1031+
1032+
start_time = time.time()
1033+
initial_timeout_stopped = False
1034+
total_timeout_stopped = False
1035+
1036+
# Run the tracing to get the trace log
1037+
# Sometimes the tracing fails due to race conditions,
1038+
# we have a quick initial timeout to catch this
1039+
while time.time() - start_time < initial_timeout:
1040+
output = gdb.read_output()
1041+
if "breakpoint 1, " in output:
1042+
initial_timeout_stopped = True
1043+
break
1044+
if not initial_timeout_stopped:
1045+
print("No initial break point found, can be a misfire, try again")
1046+
sys.exit(1)
1047+
while time.time() - start_time < total_timeout:
1048+
output = gdb.read_output()
1049+
if "PC trace complete" in output:
1050+
print("\nTrace complete")
1051+
total_timeout_stopped = True
1052+
break
1053+
if not total_timeout_stopped:
1054+
print("Final tracing timeout reached")
1055+
sys.exit(1)
1056+
1057+
# Parse and truncate the trace log to get all PCs in a list
1058+
pc_list = gdb.parse_pc_trace_file(pc_trace_file)
1059+
# Get the unique PCs and annotate their occurrence count
1060+
pc_count_dict = Counter(pc_list)
1061+
if len(pc_count_dict) <= 0:
1062+
print("Found no tracing, stopping")
1063+
sys.exit(1)
1064+
print("Trace data is logged in ", pc_trace_file)
1065+
print(
1066+
"Tracing has a total of",
1067+
len(pc_count_dict),
1068+
"unique PCs",
1069+
flush=True,
1070+
)
1071+
1072+
# Reset the target, flush the output, and close gdb
1073+
gdb = reset_target_and_gdb(gdb)
1074+
1075+
data_out = [None, None]
1076+
1077+
started = True
1078+
for pc, count in pc_count_dict.items():
1079+
for i_count in range(min(MAX_SKIPS_PER_LOOP, count)):
1080+
# Search for collisions in outputs between the cmac instances
1081+
for i in range(2):
1082+
print("-" * 80)
1083+
print(
1084+
"Applying instruction skip in ",
1085+
pc,
1086+
"occurrence",
1087+
i_count,
1088+
"data",
1089+
i,
1090+
)
1091+
print("-" * 80)
1092+
1093+
crash_observation = "crash detected"
1094+
1095+
try:
1096+
# The observation points
1097+
observations = {
1098+
# Crash check
1099+
crash_observation_address: f"{crash_observation}",
1100+
}
1101+
gdb.add_observation(observations)
1102+
1103+
gdb.apply_instruction_skip(
1104+
pc, parser.parse_next_instruction(pc), i_count
1105+
)
1106+
gdb.send_command("c", check_response=False)
1107+
1108+
# The instruction skip loop
1109+
symfi.handle_cmac(
1110+
data[i],
1111+
data_len,
1112+
key,
1113+
key_len,
1114+
iv,
1115+
cfg,
1116+
trigger,
1117+
)
1118+
testos_response = read_testos_output()
1119+
1120+
gdb_response = gdb.read_output()
1121+
data_out[i] = None
1122+
if "instruction skip applied" in gdb_response:
1123+
total_attacks += 1
1124+
1125+
if crash_observation in gdb_response:
1126+
print("Crash detected, resetting", flush=True)
1127+
gdb = reset_target_and_gdb(gdb)
1128+
else:
1129+
testos_response_json = json.loads(
1130+
testos_response
1131+
)
1132+
print(
1133+
"Output:", testos_response_json, flush=True
1134+
)
1135+
if testos_response_json["status"] == 0:
1136+
data_out[i] = tuple(
1137+
testos_response_json["data"]
1138+
)
1139+
1140+
if (
1141+
utils.is_partial_collision(
1142+
data_out[0],
1143+
data_out[1],
1144+
match_threshold_ratio=0.75,
1145+
)
1146+
) or utils.is_majority_zeros(
1147+
data_out[i], total_length=16
1148+
):
1149+
successful_faults += 1
1150+
print("-" * 80)
1151+
print("Successful FI attack!")
1152+
print(
1153+
"Location:",
1154+
pc,
1155+
"iteration",
1156+
i_count,
1157+
)
1158+
print(gdb_response)
1159+
print("Response:", testos_response_json)
1160+
print("-" * 80)
1161+
# Reset GDB by closing and opening again
1162+
gdb = reset_gdb(gdb)
1163+
else:
1164+
print(
1165+
"No break point found, something went wrong",
1166+
flush=True,
1167+
)
1168+
gdb = reset_target_and_gdb(gdb)
1169+
1170+
except json.JSONDecodeError:
1171+
print(
1172+
"Error: JSON decoding failed. Invalid response format",
1173+
flush=True,
1174+
)
1175+
try:
1176+
gdb = reset_target_and_gdb(gdb)
1177+
except TimeoutError:
1178+
gdb = re_initialize(gdb)
1179+
1180+
except TimeoutError as e:
1181+
print("Timeout error, retrying", flush=True)
1182+
print(e, flush=True)
1183+
try:
1184+
gdb = reset_target_and_gdb(gdb)
1185+
except TimeoutError:
1186+
gdb = re_initialize(gdb)
1187+
1188+
finally:
1189+
print("-" * 80)
1190+
print(
1191+
f"Total attacks {total_attacks}, successful attacks {successful_faults}"
1192+
)
1193+
# Close the OpenOCD and GDB connection at the end
1194+
if gdb:
1195+
gdb.close_gdb()
1196+
target.close_openocd()
1197+
sys.stdout = original_stdout
1198+
self.assertEqual(successful_faults, 0)
1199+
self.assertEqual(started, True)
1200+
9701201

9711202
if __name__ == "__main__":
9721203
r = Runfiles.Create()

0 commit comments

Comments
 (0)