예제 #1
0
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()
예제 #2
0
    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])
예제 #3
0
    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])
예제 #4
0
    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)
예제 #5
0
    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))
예제 #6
0
 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)
예제 #7
0
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()
예제 #8
0
    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)
예제 #9
0
    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()
예제 #10
0
    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()
예제 #11
0
    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]
예제 #12
0
    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))
예제 #13
0
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()
예제 #14
0
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()
예제 #15
0
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')
예제 #16
0
 def setUp(self):
     self.project = cw.create_project('test_project')
     traces = create_random_traces(100, 3000)
     self.project.traces.extend(traces)
예제 #17
0
 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)