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 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) # 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 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
## 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()) ) for i, n in enumerate([f"cpa{i}" for i in range(16)]): print( hex(K[i]), (K[i] == np.abs(s[n]._finalize()).max(1).argmax()) and "found !"
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()) import matplotlib.pyplot as plt _ = plt.plot(results.T) _ = plt.show()
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 - ... By default, a Session only registers MeanEngine and VarEngine. The Mean/Variance of the leakage is the only thing computed in that very case: """ print( session.engines ) # engines is a dict of all the engines registered by the Session, with key equal to the name given session.run() # Now that the engines has been fed with all the traces, we can access their results through their finalize() method: mean = session.engines["mean"].finalize() mean = session.engines["mean"].finalize() # shortcut variance = session["var"].finalize() print("mean=", mean) print("variance=", variance)