def cw_project_to_trs(project_name, trs_filename, export_keys): """Converts ChipWhisperer project into trs trace format. Args: project_name: Path to ChipWhisperer capture project. trs_filename: Output filename for trs result. export_keys: Set to true to include the keys in the trs output. """ print(f'input project: {project_name}') p = cw.open_project(project_name) print(f'num_traces: {len(p.traces)}') print(f'num_samples per trace: {len(p.waves[0])}') print(f'output file: {trs_filename}') h = gen_trs_headers(p, export_keys) calc_data_offsets(p.traces[0], export_keys, h) traces = [] for trace in tqdm(p.traces, desc='Converting', ncols=80): traces.append( trsfile.Trace(trsfile.SampleCoding.FLOAT, trace.wave, data=gen_trs_data(trace, export_keys))) print('Writing output file, this may take a while.') with trsfile.trs_open(trs_filename, 'w', engine='TrsEngine', headers=h, live_update=True) as t: t.extend(traces)
def test_project_openable(self): self.project = cw.create_project(self.project_name) traces = create_random_traces(100, 5000) self.project.traces.extend(traces) self.project.save() # make sure you can open the project with open_project self.project = cw.open_project(self.project_name)
def test_CPA(self): project = cw.open_project('projects/Tutorial_B5') leak_model = cwa.leakage_models.sbox_output attack = cwa.cpa(project, leak_model) results = attack.run() keys = results.find_key() for i in range(len(project.keys[0])): self.assertEqual(project.keys[0][i], keys[i]) project.close(save=False)
def test_jitter(self): project = cw.open_project('projects/jittertime') resync_traces = cwa.preprocessing.ResyncSAD(project) resync_traces.ref_trace = 0 resync_traces.target_window = (700, 1500) resync_traces.max_shift = 700 new_proj = resync_traces.preprocess() leak_model = cwa.leakage_models.sbox_output attack = cwa.cpa(new_proj, leak_model) results = attack.run() keys = results.find_key() for i in range(len(project.keys[0])): self.assertEqual(project.keys[0][i], keys[i]) project.close(save=False)
def __load_from_file(self, ntru_file): """ TODO: add description :param ntru_file: :return: """ gdl.check_file_exists(ntru_file+".cwp") # Open the ASCAD database HDF5 for reading # try: self.__raw_data = cw.open_project(ntru_file) self.key_num = self.__raw_data.keys[0].size # 64 self.key_ids_num = int(self.key_num/2) # 32
def __init__(self, project_file, trace_slice, attack_window, attack_direction): """Inits a TraceWorker. Args: project_file: A Chipwhisperer project file. trace_slice: Traces assigned to this worker. attack_window: Samples to process. attack_direction: Attack direction. """ self.project = cw.open_project(project_file) # TODO: Consider more efficient formats. self.num_samples = attack_window.stop - attack_window.start if attack_direction == AttackDirection.INPUT: self.texts = np.vstack(self.project.textins[trace_slice]) else: self.texts = np.vstack(self.project.textouts[trace_slice]) self.traces = np.asarray( self.project.waves[trace_slice])[:, attack_window]
def GO_Convert(CW_format_file, ASCAD_format_file): proj = cw.open_project(CW_format_file) out_file = h5py.File(ASCAD_format_file) # Generate dummy mask mask = np.zeros(16, dtype=np.uint8) # Create traces dataset out_file.create_dataset(name="traces", data=proj.waves[:], dtype=np.array(proj.waves[0]).dtype) # Prepare metadata dataset metadata_type = np.dtype([("plaintext", proj.textins[0].dtype, (len(proj.textins[0]),)), ("key", proj.keys[0].dtype, (len(proj.keys[0]),)), ("masks", mask.dtype, (len(mask),))]) traces_metadata = np.array([(proj.textins[n], proj.keys[n], mask) for n in range(proj.traces.max + 1)], dtype=metadata_type) # Create metadata dataset out_file.create_dataset("metadata", data=traces_metadata, dtype=metadata_type) out_file.flush() out_file.close()
def plot_results(plot_cfg, project_name): """Plots traces from `project_name` using `plot_cfg` settings.""" project = cw.open_project(project_name) if len(project.waves) == 0: print('Project contains no traces. Did the capture fail?') return # The ADC output is in the interval [-0.5, 0.5). Check that the recorded # traces are within that range with some safety margin. if not (np.all(np.greater(project.waves, -plot_cfg["amplitude_max"])) and np.all(np.less(project.waves, plot_cfg["amplitude_max"]))): print('WARNING: Some traces have samples outside the range (' + str(-plot_cfg["amplitude_max"]) + ', ' + str(plot_cfg["amplitude_max"]) + ').') print('The ADC has a max range of [-0.5, 0.5) and might saturate.') print('It is recommended to reduce the scope gain (see device.py).') plot.save_plot_to_file(project.waves, plot_cfg["num_traces"], plot_cfg["trace_image_filename"]) print( f'Created plot with {plot_cfg["num_traces"]} traces: {Path(plot_cfg["trace_image_filename"]).resolve()}' )
#!/usr/bin/python3 import chipwhisperer as cw import chipwhisperer.analyzer as cwa import sys import getopt proj = cw.open_project(sys.argv[1]) print("Project loaded...") attack = cwa.cpa(proj, cwa.leakage_models.sbox_output) attack.points_range = [75483, 75483 + 33206] print("Begin CPA job") results = attack.run() print(results.find_maximums()) print(results.best_guesses())
# SPDX-License-Identifier: Apache-2.0 import binascii import chipwhisperer as cw from chipwhisperer.analyzer.attacks.attack_mix_columns import AttackMixColumns import scared import numpy as np PROJECTS=[ 'projects/opentitan_simple_aes_0', 'projects/opentitan_simple_aes_1', 'projects/opentitan_simple_aes_2', 'projects/opentitan_simple_aes_3', ] print('loading projects') projects = [cw.open_project(p) for p in PROJECTS] attack = AttackMixColumns(projects) results = attack.run() known_key_bytes = projects[0].keys[0] key_guess_bytes = results['guess'] known_key = binascii.b2a_hex(bytearray(known_key_bytes)) print('known_key: {}'.format(known_key)) key_guess = binascii.b2a_hex(bytearray(key_guess_bytes)) print('key guess: {}'.format(key_guess)) if key_guess != known_key: num_bytes_match = 0
return max_rho, rho def bit_count(number): bit_count = 0 while number: number &= (number - 1) bit_count += 1 return bit_count if __name__ == '__main__': # Open trace file. project_file = 'projects/opentitan_simple_aes' project = cw.open_project(project_file) num_traces = len(project.waves) num_samples = len(project.waves[0]) # Create a local, dense copy of the traces. This makes the remaining # operations much faster. traces = np.empty((num_traces, num_samples_use), np.double) for i_trace in range(num_traces): traces[i_trace] = project.waves[i_trace][ start_sample_use:stop_sample_use] ############################ # Filter out noisy traces. # ############################
def perform_attack(project_file, num_traces, attack_window, attack_direction, max_std, num_workers): """Performs a correlation-enhanced power analysis collision attack. This function: - Computes the mean and standard deviation of all traces (*), - Filters noisy traces (*), - Computes mean traces for all values of all plaintext/ciphertext bytes (*), - Guesses differences between each key byte, and - Recovers the key using these differences. Steps marked with (*) above are implemented in a distributed manner: After creating ``num_workers`` number of ``TraceWorker`` instances, this function assigns a subset of traces to each worker and aggregates their results to be used in the subsequent steps of the attack. See "Correlation-Enhanced Power Analysis Collision Attack" by A. Moradi, O. Mischke, and T. Eisenbarth (https://eprint.iacr.org/2010/297.pdf) for more information. Args: project_file: A Chipwhisperer project file. num_traces: Number of traces to use, must be less than or equal to the number of traces in ``project_file``. attack_window: Attack window as a pair of sample indices, inclusive. attack_direction: Attack direction. max_std: Allowed number of standard deviations from the mean trace for filtering noisy traces. num_workers: Number of workers to use for processing traces. Returns: Recovered key if the attack was successful, ``None`` otherwise. """ project = cw.open_project(project_file) # Check arguments num_total_traces = len(project.waves) if num_traces > num_total_traces: raise ValueError( f"Invalid num_traces: {num_traces} (must be less than {num_total_traces})" ) last_sample = len(project.waves[0]) - 1 if min(attack_window) < 0 or max(attack_window) > last_sample: raise ValueError( f"Invalid attack window: {attack_window} (must be in [0, {last_sample}])" ) if max_std <= 0: raise ValueError( f"Invalid max_std: {max_std} (must be greater than zero)") if num_workers <= 0: raise ValueError( f"Invalid num_workers: {num_workers} (must be greater than zero)") # Instantiate workers def worker_trace_slices(): """Determines the traces of each worker. Assigns the remainder, if any, to the first worker. """ traces_per_worker = int(num_traces / num_workers) first_worker_num_traces = traces_per_worker + num_traces % num_workers yield slice(0, first_worker_num_traces) for trace_begin in range(first_worker_num_traces, num_traces, traces_per_worker): yield slice(trace_begin, trace_begin + traces_per_worker) # Attack window is inclusive. attack_window = slice(attack_window[0], attack_window[1] + 1) workers = [ TraceWorker.remote(project_file, trace_slice, attack_window, attack_direction) for trace_slice in worker_trace_slices() ] assert len(workers) == num_workers # Compute mean and standard deviation. mean, std_dev = compute_mean_and_std(workers) # Filter noisy traces. orig_num_traces = num_traces num_traces = filter_noisy_traces(workers, mean, std_dev, max_std) logging.info(f"Will use {num_traces} traces " f"({100*num_traces/orig_num_traces:.1f}% of all traces)") # Mean traces for all values of all text bytes. mean_text_traces = compute_mean_text_traces(workers) # Guess the differences between key bytes. pairwise_diffs_scores = compute_pairwise_diffs_and_scores(mean_text_traces) diffs = find_best_diffs(pairwise_diffs_scores) logging.info(f"Difference values (delta_0_i): {diffs}") # Recover the key. key = recover_key(diffs, attack_direction, project.textins[0], project.textouts[0]) if key is not None: logging.info(f"Recovered AES key: {bytes(key).hex()}") else: logging.error("Failed to recover the AES key") # Compare differences - both matrices are symmetric and have an all-zero main diagonal. correct_diffs = compare_diffs(pairwise_diffs_scores, attack_direction, project.keys[0]) logging.info(f"Recovered {((np.sum(correct_diffs)-16)/2).astype(int)}/120 " "differences between key bytes") return key
def test_trace_beyond_segment(self): self.project = cw.open_project('test_seg') arr = bytearray(b'CWUNIQUESTRING2') for i in range(0, len(arr)): self.assertEqual(self.project.textins[10000][i], arr[i])
plt.show() #plt.savefig('../collections/May_18_RevisedFirmware/Key{}_wave0.png'.format(i)) print("Key", i, "finished", "Imagin Saved") plt.clf() ''' # convert to cvs for trace in proj.traces: append_list_as_row('../collections/May_18_RevisedFirmware/key{}_trace.csv'.format(i), trace.wave) append_list_as_row('../collections/May_18_RevisedFirmware/key{}_key.csv'.format(i), trace.key) append_list_as_row('../collections/May_18_RevisedFirmware/key{}_textin.csv'.format(i), trace.textin) append_list_as_row('../collections/May_18_RevisedFirmware/key{}_textout.csv'.format(i), trace.textout) ''' #Doing attack using CPA results = [] for i in range(0, 32): proj = cw.open_project( "../collections/May_18_RevisedFirmware/Key_{}.cwp".format(i)) # Attack part, makesure each key is woring fine attack = cwa.cpa(proj, leak_model) result = attack.run() print("Attack finshed, for Key", i, "result is:") print(result) results.append(result) try: assert (result.find_key() == proj.keys[0]) except: print("result", i, "is wrong:") print("except:", proj.keys[0]) print("result:", result.find_key())