Acquisition setup exampleΒΆ

In this tutorial we will show how to set up an AbstractContainer dedicated to emulate a simple (dummy) side-channel acquisition setup. The context here is the use of lascar for the acquisition phase.

Say we are targeting a device running an AES, that we can drive through a python class Dut (for Device Under Test). This class has a method that commands the device to run the targeted operation, and returns the plaintext/ciphertext.

import numpy as np

class Dut:
    def go(self):
        # Dummy AES, returns the plaintext and the ciphertext, concatenated.
        plaintext = np.random.randint(0, 256, (16,))  # 16 random bytes
        ciphertext = np.random.randint(0, 256, (16,))
        return np.append(plaintext, ciphertext)

Beside, we have an Oscilloscope class, which returns the power measurement of the device through the method get_trace():

class Oscilloscope:
    def get_trace(self):
        # Return the side-channel leakage: here a vector of 100 floats
        return np.random.rand(100)

We create an AcquisitionSetup class, inheriting from AbstractContainer, and override the generate_trace() method which will request both device and oscilloscope.

from lascar import AbstractContainer, Trace

class AcquisitionSetup(AbstractContainer):
    def __init__(self, number_of_traces: int):
        """
        :param number_of_traces: Number of traces in the container
        """
        super().__init__(self, number_of_traces)
        self.dut = Dut()
        self.oscilloscope = Oscilloscope()

    def generate_trace(self, index: int):
        """
        Method required by `AcquisitionSetup` to work properly.

        :param index: index of trace, not used here.
        :return: Trace with leakage from oscilloscope, and value from the dut
            plaintext/ciphertext.
        """
        # The container logger can be used!
        self.logger.debug("Generate trace %d", index)
        value = self.dut.go()
        leakage = self.oscilloscope.get_trace()
        return Trace(leakage, value)

Now everything is ready to create our AcquisitionSetup. This container is Abstract. Its 100 traces are stored nowhere, yet they can be accessed:

acquisition = AcquisitionSetup(100)
print("trace 0:", acquisition_container[0])
print("trace 10:", acquisition_container[10])

More importantly, this container can be converted to a Hdf5Container, so the traces get saved to the disk. The export method takes here all its sense.

from lascar import Hdf5Container

hdf5 = Hdf5Container.export(acquisition, "tmp.h5")