def export(container, leakages_filename, values_filename, name=None, batch_size=100): """ export method is used to export an existing container into an NpyContainer. It creates a session and use a ContainerDumpEngine to recopy the traces delivered by the original container into a NpyContainer. :param container: container to be exported :param leakages_filename: name of the npy file to create corresponding to the leakages :param values_filename: name of the npy file to create corresponding to the values :param name: name for the session :param batch_size: batch_size for the session :return: """ leakage, value = container[0] out = NpyContainer._void_container(leakages_filename, values_filename, container.number_of_traces, leakage.shape, leakage.dtype, value.shape, value.dtype) from lascar import Session from lascar.engine import ContainerDumpEngine session = Session(container, engine=ContainerDumpEngine(out), name=name if name else 'NpyContainer') session.run(batch_size) return out
def export( container, filename, name=None, leakages_dataset_name="leakages", values_dataset_name="values", batch_size=100, ): """ export method is used to export an existing container into an Hdf5Container. It creates a session and use a ContainerDumpEngine to recopy the traces delivered by the original container. :param container: container to be exported :param filename: name of the Hdf5Container to vuild :param leakages_dataset_name: :param values_dataset_name: :param batch_size: :return: """ leakage, value = container[0] out = Hdf5Container.void_container( filename, container.number_of_traces, leakage.shape, leakage.dtype, value.shape, value.dtype, leakages_dataset_name, values_dataset_name, ) from lascar import Session from lascar.engine import ContainerDumpEngine session = Session( container, engine=ContainerDumpEngine(out), name=name if name else "Hdf5Container", ) session.run(batch_size) # Adding mean/var: to leakage_dataset_name as attributes try: out._file[leakages_dataset_name].attrs["mean"] = session[ "mean"].finalize() out._file[leakages_dataset_name].attrs["var"] = session[ "var"].finalize() except: pass return out
def get_leakage_mean_var(self): """ Compute mean/var of the leakage. :return: mean/var of the container leakages """ from lascar import Session session = Session(self).run() return session['mean'].finalize(), session['var'].finalize()
def show_nicv(values, traces, nr_digits=4): """ Compute the Normalized Inter-Class Variance as in the article """ import numpy as np values = np.array(values, dtype=np.uint8) traces = np.array(traces) from lascar import TraceBatchContainer, Session, NicvEngine, hamming # Use the Hamming weight of leaked values and add some noise # through the 'leakage_processing' parameter t = TraceBatchContainer( traces, values, leakage_processing=lambda x: np.array( [hamming(i) for i in x], dtype=np.uint ) + np.random.normal(0, 0.5, len(x)), ) s = Session(t) ## Input value leakage # s.add_engines([NicvEngine('a'+str(i), lambda v,z=i:v[z], range(9)) for i in range(nr_digits)]) ## Difference leakage s.add_engines([NicvEngine('a'+str(i), lambda v,z=i:np.int8(v[z]) - np.int8(STORED_PIN[z]), range(-9,8)) for i in range(nr_digits)]) ## below is a variant on the carry bit # s.add_engines([NicvEngine('c'+str(i), lambda v,z=i:v[z]>int(STORED_PIN[z]), range(16)) for i in range(nr_digits)]) s.run() return np.array([s[eng]._finalize() for eng in s.engines if eng not in ['mean', 'var']])
def export(container, filename, name=None, leakages_dataset_name="leakages", values_dataset_name="values", batch_size=100): """ export method is used to export an existing container into an Hdf5Container. It creates a session and use a ContainerDumpEngine to recopy the traces delivered by the original container. :param container: container to be exported :param filename: name of the Hdf5Container to vuild :param leakages_dataset_name: :param values_dataset_name: :param batch_size: :return: """ leakage, value = container[0] out = Hdf5Container.void_container(filename, container.number_of_traces, leakage.shape, leakage.dtype, value.shape, value.dtype, leakages_dataset_name, values_dataset_name) from lascar import Session from lascar.engine import ContainerDumpEngine session = Session(container, engine=ContainerDumpEngine(out), name=name if name else 'Hdf5Container') session.run(batch_size) return out
for i, t in enumerate(traces): # pad to longest trace (even though they might well be all the same length) tmp[i][: len(t)] = np.array(t, dtype=np.float32) traces = tmp ## Phase 2 : attack with Lascar ## Launch 16 CPAs on the outputs of the sbox from lascar.container import TraceBatchContainer from lascar import Session, CpaEngine, ConsoleOutputMethod from lascar.tools.aes import sbox t = TraceBatchContainer(traces, values) s = Session(t, output_method=ConsoleOutputMethod()) s.add_engines( [ CpaEngine(f"cpa{i}", lambda v, k, z=i: sbox[v[z] ^ k], range(256)) for i in range(16) ] ) s.run() print(s.output_method.finalize()) # Check the results : print("Key should be : f0 33 1c e0 26 6a da ce 86 a8 a1 3b fa 14 67 40") K = list( map(lambda x: int(x, 16), "f0 33 1c e0 26 6a da ce 86 a8 a1 3b fa 14 67 40".split())
values = np.array(values, dtype=np.uint8) lgst_dim = max(map(len, traces)) # we're gonna split each 32bit value in 8 bit chunks lgst_dim *= 4 tmp = np.zeros((len(traces), lgst_dim), dtype=np.float32) for i, t in enumerate(traces): for x in range(len(t)): for j in range(4): tmp[i][x * 4 + j] = (t[x] >> (8 * j)) & 0xFF return values, tmp, addresses if __name__ == "__main__": _, func = generate_targetf() values, traces, addresses = get_traces(func, 10, 1000000) from lascar import TraceBatchContainer, Session t = TraceBatchContainer(traces, values) s = Session(t) s.run() from rainbow.utils.plot import viewer viewer(addresses, s["var"].finalize())
from lascar.tools.aes import sbox def selection_function(value, guess): return sbox[value['plaintext'][3] ^ guess] & 1 guess_range = range(256) from lascar import DpaEngine dpa_engine = DpaEngine('dpa', selection_function, guess_range) # We can now create a Session, register the dpa_lsb_engine, and run it. from lascar import Session session = Session(container, engine=dpa_engine) # session.add_engine( dpa_lsb_engine) # the engine can be added after the session creation session.run( batch_size=100) # the session will load traces by batches of 100 traces """ Now, to get the result, one solution could be to request the dpa_lsb_engine.finalize() method. (As most of the engines, the finalize() method returns sca results) For more option about how to manage results of sca, please follow the next step of the tutorial. """ results = dpa_engine.finalize() print(results.shape) print("Best Guess is %02X." % results.max(1).argmax())
self.device["r1"] = 16 # second argument of the subbytes function self.device.start(self.device.functions["subbytes"], 0) # rainbow launch the "subbytes" function # we prepare the leakage for this trace: leakage = np.fromiter( map(hamming_weight, self.device.sca_values_trace), dtype=np.uint8 ) # a hamming_weight model is applied to the rainbow-generated leakage leakage = (np.random.normal(0, self.noise, len(leakage)) + leakage ) # artificial noise is added to the leakage return Trace(leakage, self.value) if __name__ == "__main__": container = RainbowSubBytesContainer(250) engine = CpaEngine( "cpa byte 5", lambda value, secret: sbox[value["plaintext"][5] ^ secret], range(256), solution=42, ) session = Session( container, engine, output_method=ScoreProgressionOutputMethod(engine), output_steps=20, ).run()
(For the rest of the tutorial, we will use a BasicAesSimulationContainer as trace Container) """ # First we need a Conainer, let's use BasicAesSimulationContainer. Say with 10000 traces, and the noise set to 1. from lascar import BasicAesSimulationContainer container = BasicAesSimulationContainer(10000, noise=1) """ The only required argument for a Session is the Container it works on. (Other optional keywords argument will be presented later) """ from lascar import Session session = Session(container) # Session class implement a logger (like Container), whose level can be set by user: session.logger.setLevel("WARN") """ The main method for Session class is the run() method. It consists in reading traces from the Container by batches. The size of batches can be specified as an argument. Once the run() method is called, all traces are read and processed by the Engines registered by the Session. Engines are classes dedicated to compute stuffs from side-channel traces. The Session distributes traces batches to all its Engines, for them to do their jobs. Here a list of Engines already implemented in lascar: - MeanEngine: compute the mean of all the traces leakages - VarEngine: compute the varianc of all the traces leakages - SnrEngine: compute Signal-to-Noise-Ratio from the traces leakages and a partitioning function appllied to the values. - CpaEngine: compute Correlation Power Analysis from the traces leakages and a guess function appllied to the values. - TTestEngine: compute Welch's T-Test from the traces leakages and a partitioning function appllied to the value