def run_capture(capture_cfg, ot, ktp): """Run ChipWhisperer capture. Args: capture_cfg: Dictionary with capture configuration settings. ot: Initialized OpenTitan target. ktp: Key and plaintext generator. """ key, text = ktp.next() cipher = AES.new(bytes(key), AES.MODE_ECB) print(f'Using key: {binascii.b2a_hex(bytes(key))}') project_file = capture_cfg['project_name'] project = cw.create_project(project_file, overwrite=True) for i in tqdm(range(capture_cfg['num_traces']), desc='Capturing', ncols=80): key, text = ktp.next() ret = cw.capture_trace(ot.scope, ot.target, text, key, ack=False) if not ret: print('Failed capture') continue expected = binascii.b2a_hex(cipher.encrypt(bytes(text))) got = binascii.b2a_hex(ret.textout) assert (got == expected), ( f'Incorrect encryption result!\ngot: {got}\nexpected: {expected}\n' ) project.traces.append(ret) ot.target.flush() project.save()
def test_short_segments(self): self.project = cw.create_project(self.project_name) self.project.seg_len = 2 self.project.seg_ind_max = self.project.traces.seg_len - 1 traces = create_random_traces(100, 1000) self.project.traces.extend(traces) index = random.randrange(0, len(traces)) index_wave = random.randrange(0, len(traces[0])) self.assertEqual(traces[index].wave[index_wave], self.project.traces[index].wave[index_wave]) # Note: I think we can just call assertEqual on the arrays instead of doing it per each value for i in range(16): # check the plaintext matches self.assertEqual(traces[index].textin[i], self.project.traces[index].textin[i]) # check the textout matches self.assertEqual(traces[index].textout[i], self.project.traces[index].textout[i]) # check the key matches self.assertEqual(traces[index].key[i], self.project.traces[index].key[i])
def test_traces_are_retrievable(self): self.project = cw.create_project(self.project_name) # make sure textin is still textin and not key, etc. traces = create_random_traces(100, 1000) self.project.traces.extend(traces) # retrieve a random trace index = random.randrange(0, len(traces)) # retrieve a random part of the power trace index_wave = random.randrange(0, len(traces[0].wave)) # check that the power trace matches self.assertEqual(traces[index].wave[index_wave], self.project.traces[index].wave[index_wave]) # Note: I think we can just call assertEqual on the arrays instead of doing it per each value for i in range(16): # check the plaintext matches self.assertEqual(traces[index].textin[i], self.project.traces[index].textin[i]) # check the textout matches self.assertEqual(traces[index].textout[i], self.project.traces[index].textout[i]) # check the key matches self.assertEqual(traces[index].key[i], self.project.traces[index].key[i])
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_remove_project(self): self.project = cw.create_project(self.project_name) # must supply i_am_sure argument. self.assertRaises(RuntimeWarning, self.project.remove) self.assertTrue(os.path.exists(self.project.datadirectory)) self.project.remove(i_am_sure=True) self.assertFalse(os.path.exists(self.project.datadirectory))
def setUp(self): self.project_name = 'testing' self.project = cw.create_project(self.project_name) self.fake_trace = cw.Trace(np.array([i for i in range(35)]), 'asdf', 'sdaf', 'sdf') self.project.traces.seg_ind_max = 4 self.trace_num = 13 for i in range(self.trace_num-1): self.project.traces.append(self.fake_trace) self.fake_trace_2 = cw.Trace(np.array([i for i in range(35)]), 'asdf', 'sdaf', 'hello') self.project.traces.append(self.fake_trace_2)
def capture_loop(trace_gen, capture_cfg): """Main capture loop. Args: trace_gen: A trace generator. capture_cfg: Capture configuration. """ project = cw.create_project(capture_cfg["project_name"], overwrite=True) for _ in tqdm(range(capture_cfg["num_traces"]), desc='Capturing', ncols=80): project.traces.append(next(trace_gen)) project.save()
def test_numpy_conversion(self): self.project = cw.create_project(self.project_name) traces = create_random_traces(100, 1000) self.project.traces.extend(traces) np_waves = np.array(self.project.waves) self.assertEqual(np.shape(np_waves), (len(traces), len(traces[0].wave))) self.assertEqual(np_waves.dtype, 'float64') for i in range(len(np_waves)): self.assertEqual((np_waves[i,:] == self.project.waves[i]).all(), True)
def setUp(self): self.project = cw.create_project('test_seg', overwrite=True) for i in range(0, 10000): arr = bytearray(b'CWUNIQUESTRING1') tr = cw.Trace(np.array([0]), arr, arr, arr) self.project.traces.append(tr) arr = bytearray(b'CWUNIQUESTRING2') tr = cw.Trace(np.array([0]), arr, arr, arr) self.project.traces.append(tr) self.project.save() self.project.close()
def test_create_and_save_project(self): self.project = cw.create_project(self.project_name) self.assertTrue(os.path.isdir(self.project_name + '_data')) trace = cw.Trace(np.array([i for i in range(100)]), 'text in', 'text out', 'key') for i in range(500): self.project.traces.append(trace) self.project.save() self.assertTrue(os.path.exists(ensure_cwp_extension(self.project_name))) # calling it again should not cause issues. self.project.save()
def setUp(self): self.project_name = 'projects/test_project' self.project = cw.create_project(self.project_name) self.traces = create_random_traces(100, 5000) self.project.traces.extend(self.traces) self.zipfile_path = 'exported_test_project.zip' file_paths = list() file_paths.append(os.path.join(ensure_cwp_extension(os.path.split(self.project_name)[1]))) dir_containing_project = os.path.abspath(os.path.join(self.project.datadirectory, '..')) for root, dirs, files in os.walk(self.project.datadirectory): for file in files: file_paths.append(os.path.relpath(os.path.join(root, file), dir_containing_project)) self.file_paths = [pathlib.Path(x).as_posix() for x in file_paths]
def test_individual_iterables(self): self.project = cw.create_project(self.project_name) # make sure textin is still textin and not key, etc. traces = create_random_traces(50, 1) self.project.traces.extend(traces) index = 0 for wave, textin, textout, key in zip(self.project.waves, self.project.textins, self.project.textouts, self.project.keys): self.assertEqual(traces[index].textin, textin) self.assertEqual(traces[index].textout, textout) self.assertEqual(traces[index].key, key) self.assertEqual(traces[index].wave, wave) index += 1 self.assertEqual(index, len(self.project.textins))
def run_batch_capture(capture_cfg, ot, ktp, scope): """Captures traces using the 'b' (batch encryption) command and WaveRunner. Args: capture_cfg: Dictionary with capture configuration settings. ot: Initialized OpenTitan target. ktp: Key and plaintext generator. scope: Scope to use for capture. """ # Set key assert ktp.fixed_key key = ktp.next_key() print(f"Using key: '{key.hex()}'.") ot.target.simpleserial_write("k", key) # Seed the target's PRNG (host's PRNG is currently seeded in main). ot.target.simpleserial_write( "s", capture_cfg["batch_prng_seed"].to_bytes(4, "little")) # Create the ChipWhisperer project. project_file = capture_cfg["project_name"] project = cw.create_project(project_file, overwrite=True) # Capture traces. rem_num_traces = capture_cfg["num_traces"] num_segments_storage = 1 with tqdm(total=rem_num_traces, desc="Capturing", ncols=80, unit=" traces") as pbar: while rem_num_traces > 0: # Determine the number of traces for this batch and arm the oscilloscope. scope.num_segments = min(rem_num_traces, scope.num_segments_max) scope.arm() # Start batch encryption. ot.target.simpleserial_write( "b", scope.num_segments_actual.to_bytes(4, "little")) # Transfer traces waves = scope.capture_and_transfer_waves() assert waves.shape[0] == scope.num_segments # Generate plaintexts and ciphertexts for the batch. # Note: Plaintexts are encrypted in parallel. plaintexts = [ ktp.next()[1] for _ in range(scope.num_segments_actual) ] ciphertexts = [ bytearray(c) for c in scared.aes.base.encrypt( np.asarray(plaintexts), np.asarray(key)) ] # Check the last ciphertext of this batch to make sure we are in sync. actual_last_ciphertext = ot.target.simpleserial_read("r", 16, ack=False) expected_last_ciphertext = ciphertexts[-1] assert actual_last_ciphertext == expected_last_ciphertext, ( f"Incorrect encryption result!\n" f"actual: {actual_last_ciphertext}\n" f"expected: {expected_last_ciphertext}") # Make sure to allocate sufficient memory for the storage segment array during the # first resize operation. By default, the ChipWhisperer API starts every new segment # with 1 trace and then increases it on demand by 25 traces at a time. This results in # frequent array resizing and decreasing capture rate. # See addWave() in chipwhisperer/common/traces/_base.py. if project.traces.cur_seg.tracehint < project.traces.seg_len: project.traces.cur_seg.setTraceHint(project.traces.seg_len) # Only keep the latest two trace storage segments enabled. By default the ChipWhisperer # API keeps all segments enabled and after appending a new trace, the trace ranges are # updated for all segments. This leads to a decreasing capture rate after time. # See: # - _updateRanges() in chipwhisperer/common/api/TraceManager.py. # - https://github.com/newaetech/chipwhisperer/issues/344 if num_segments_storage != len(project.segments): if num_segments_storage >= 2: project.traces.tm.setTraceSegmentStatus( num_segments_storage - 2, False) num_segments_storage = len(project.segments) # Add traces of this batch to the project. for wave, plaintext, ciphertext in zip(waves, plaintexts, ciphertexts): project.traces.append( cw.common.traces.Trace(wave, plaintext, bytearray(ciphertext), key)) # Update the loop variable and the progress bar. rem_num_traces -= scope.num_segments pbar.update(scope.num_segments) # Before saving the project, re-enable all trace storage segments. for s in range(len(project.segments)): project.traces.tm.setTraceSegmentStatus(s, True) assert len(project.traces) == capture_cfg["num_traces"] # Save the project to disk. project.save()
def run_batch_capture(capture_cfg, ot, ktp, scope): """Captures traces using the 'b' (batch encryption) command and WaveRunner. Args: capture_cfg: Dictionary with capture configuration settings. ot: Initialized OpenTitan target. ktp: Key and plaintext generator. scope: Scope to use for capture. """ # Set key assert ktp.fixed_key key = ktp.next_key() print(f"Using key: '{key.hex()}'.") ot.target.simpleserial_write("k", key) # Seed the target's PRNG (host's PRNG is currently seeded in main). ot.target.simpleserial_write( "s", capture_cfg["batch_prng_seed"].to_bytes(4, "little")) # Create the ChipWhisperer project. project_file = capture_cfg["project_name"] project = cw.create_project(project_file, overwrite=True) # Capture traces. rem_num_traces = capture_cfg["num_traces"] with tqdm(total=rem_num_traces, desc="Capturing", ncols=80, unit=" traces") as pbar: while rem_num_traces > 0: # Determine the number of traces for this batch and arm the oscilloscope. scope.num_segments = min(rem_num_traces, scope.num_segments_max) scope.arm() # Start batch encryption. ot.target.simpleserial_write( "b", scope.num_segments_actual.to_bytes(4, "little")) # Transfer traces waves = scope.capture_and_transfer_waves() assert waves.shape[0] == scope.num_segments # Generate plaintexts and ciphertexts for the batch. # Note: Plaintexts are encrypted in parallel. plaintexts = [ ktp.next()[1] for _ in range(scope.num_segments_actual) ] ciphertexts = [ bytearray(c) for c in scared.aes.base.encrypt( np.asarray(plaintexts), np.asarray(key)) ] # Check the last ciphertext of this batch to make sure we are in sync. actual_last_ciphertext = ot.target.simpleserial_read("r", 16, ack=False) expected_last_ciphertext = ciphertexts[-1] assert actual_last_ciphertext == expected_last_ciphertext, ( f"Incorrect encryption result!\n" f"actual: {actual_last_ciphertext}\n" f"expected: {expected_last_ciphertext}") # Add traces of this batch to the project. # TODO: This seems to scale with the total number of traces, not just the number of # new traces. We should take a closer look. for wave, plaintext, ciphertext in zip(waves, plaintexts, ciphertexts): project.traces.append( cw.common.traces.Trace(wave, plaintext, bytearray(ciphertext), key)) # Update the loop variable and the progress bar. rem_num_traces -= scope.num_segments pbar.update(scope.num_segments) assert len(project.traces) == capture_cfg["num_traces"] project.save()
leak_model = cwa.leakage_models.sbox_output num_traces = 10 def append_list_as_row(file, item): with open(file, 'a+', newline='') as write_obj: csv_writer = writer(write_obj) csv_writer.writerow(item) for i in range(0, 1): # loop for each different key print("working on key", i, ":") key = ktp.next_key() # Next Key proj = cw.create_project( "../collections/May_18_RevisedFirmware/Key_{}.cwp".format(i), overwrite=True) # collection for each key for j in progressbar.progressbar(range(num_traces)): text = ktp.next_text() # next textin trace = cw.capture_trace(scope, target, text, key) while (trace is None): trace = cw.capture_trace(scope, target, text, key) proj.traces.append(trace) proj.save() # Sample Trace Part plt.plot(proj.waves[0]) plt.xlabel('Time') plt.ylabel('Amplitude')
def setUp(self): self.project = cw.create_project('test_project') traces = create_random_traces(100, 3000) self.project.traces.extend(traces)
def setUp(self): self.project = cw.create_project('test_project') self.traces = create_random_traces(1000, 5000) for trace in self.traces: self.project.traces.append(trace)