コード例 #1
0
    def run_pooled(self,
                   num_points_of_interest: int,
                   spacing_points_of_interest: int,
                   selected_subkey: int,
                   bar: progressbar.ProgressBar,
                   gpu: bool,
                   leakage_model: bool,
                   debug_mode_enabled: bool = False) -> List[int]:
        """ Run method used to run the pooled Template Attack.

        :param num_points_of_interest: number of points of interest to use
        :param spacing_points_of_interest: spacing between the points of interest
        :param selected_subkey: the subkey index to analyze. Must be in the range [0-16]
        :param bar: the progressbar to update
        :param gpu: whether or not to use gpu for this attack
        :param leakage_model: the leakage model to use
        :param debug_mode_enabled: whether to enable debug mode
        :return: list containing the calculated (sub)key
        """

        print('Pooled template attack is being executed...')

        self.log_handler = LogHandler('pooled_ta', debug_mode_enabled)

        self.log_handler.log_to_debug_file(
            'RUNNING pooled template attack with \n'
            'TEMPLATE_TRACES: {} \n'
            'NUM_FEATURES: {} \n'
            'ATTACK_TRACES: {} \n'
            'SUBKEY: {}.'.format(len(self.template_traces),
                                 num_points_of_interest,
                                 len(self.attack_traces), selected_subkey))

        num_attack_traces = len(self.attack_traces)
        result = [0 for _ in range(16)]

        mean_list, poi_list, pooled_covariance_matrix = \
            self.pooled_covariance_matrix(num_points_of_interest, spacing_points_of_interest, gpu, leakage_model)

        if selected_subkey == 16:
            for x in range(16):
                template_means = mean_list[x]
                points_of_interest = poi_list[x]

                subkey_result = self.pooled_solve_subkey(
                    x, points_of_interest, template_means,
                    num_points_of_interest, num_attack_traces,
                    pooled_covariance_matrix, bar)

                result[x] = subkey_result
        else:
            template_means = mean_list[selected_subkey]
            points_of_interest = poi_list[selected_subkey]

            result[selected_subkey] = self.pooled_solve_subkey(
                selected_subkey, points_of_interest, template_means,
                num_points_of_interest, num_attack_traces,
                pooled_covariance_matrix, bar)

        return result
コード例 #2
0
    def solve_subkey(self,
                     subkey: int,
                     debug_mode_enabled: bool = False,
                     round_: int = 1,
                     operation: int = 0,
                     bar: progressbar.ProgressBar = None):
        """ Solve a subkey
        :param subkey: the subkey index to analyze. Must be in the range [0-15]
        :param debug_mode_enabled: whether to enable debug mode
        :param round_: round of aes.
        :param operation: operation of aes.
        :param bar: the progressbar to update
        :return: the calculated subkey corresponding to the subkey index specified
        """

        log_handler = LogHandler("mia", debug_mode_enabled)

        log_handler.log_to_debug_file('RUNNING dpa attack with \n'
                                      'NUM_FEATURES: {} \n'
                                      'ATTACK_TRACES: {} \n'
                                      'SUBKEY: {}.'.format(
                                          self.RANGE, self.NUMBER_OF_TRACES,
                                          subkey))
        differential = np.zeros(self.KEY_SIZE)
        means = np.zeros((self.RANGE, 2))

        for key in range(self.KEY_SIZE):

            self.keys[:, subkey] = key
            leakage_table = HammingWeight.bitwise_hamming(
                self.plain, self.keys, subkey, round_, operation, self.bit)

            list1 = []
            list0 = []
            for i in range(self.NUMBER_OF_TRACES):
                if leakage_table[i] == 1:
                    list1.append(i)
                else:
                    list0.append(i)
            if self.OFFSET == 0:
                for i in range(self.RANGE):
                    means[i][0] = np.mean(self.traces[list0, i])
                    means[i][1] = np.mean(self.traces[list1, i])
            else:
                for i in range(self.RANGE - self.OFFSET):
                    means[i][0] = np.mean(
                        np.abs(self.traces[list0, i] -
                               self.traces[list0, i + self.OFFSET]))
                    means[i][1] = np.mean(
                        np.abs(self.traces[list1, i] -
                               self.traces[list1, i + self.OFFSET]))
            temp = np.zeros(self.RANGE - self.OFFSET)
            for i in range(self.RANGE - self.OFFSET):
                temp[i] = np.abs(means[i][1] - means[i][0])
            differential[key] = np.max(temp)
            if bar is not None and bar.value < bar.max_value:
                bar.update(bar.value + 1)

        return np.argmax(differential)
コード例 #3
0
    def solve_subkey(self,
                     subkey: int,
                     hamming_leakage: bool = True,
                     debug_mode_enabled: bool = False,
                     round_: int = 1,
                     operation: int = 0,
                     bar: progressbar.ProgressBar = None,
                     conditional_averaging: bool = False):
        """ Solve a subkey
        :param subkey: the subkey index to analyze. Must be in the range [0-15]
        :param hamming_leakage: whether to use the hamming_weight leakage model
        :param debug_mode_enabled: whether to enable debug mode
        :param round_: round of aes.
        :param operation: operation of aes.
        :param bar: the progressbar to update
        :param conditional_averaging: whether to use conditional averaging or not.
        :return: the calculated subkey corresponding to the subkey index specified
        """

        num_values = 256
        if hamming_leakage:
            num_values = 9

        if conditional_averaging:
            return CPA.conditional_avg(self, subkey, hamming_leakage, round_,
                                       operation, bar)

        log_handler = LogHandler("cpa", debug_mode_enabled)

        log_handler.log_to_debug_file('RUNNING cpa attack with \n'
                                      'NUM_FEATURES: {} \n'
                                      'ATTACK_TRACES: {} \n'
                                      'SUBKEY: {}.'.format(
                                          self.RANGE, self.NUMBER_OF_TRACES,
                                          subkey))
        correlation = np.zeros(self.KEY_SIZE)

        for key in range(self.KEY_SIZE):

            for i in range(len(self.traces)):
                self.keys[i][subkey] = key
            leakage_table = HammingWeight.hamming_weights(
                self.plain, self.keys, subkey, round_, operation,
                hamming_leakage) / num_values - 1

            if self.online:
                correlation[key] = max(self.online_coeffs(leakage_table))
            else:
                correlation[key] = max(
                    Pearson.pearson_cpa(leakage_table, self.traces))
            if bar is not None and bar.value < bar.max_value:
                bar.update(bar.value + 1)
        return np.argmax(correlation)
コード例 #4
0
    def run_normal(self,
                   num_points_of_interest: int,
                   spacing_points_of_interest: int,
                   selected_subkey: int,
                   bar: progressbar.ProgressBar,
                   gpu: bool,
                   leakage_model: bool,
                   debug_mode_enabled: bool = False) -> List[int]:
        """ Run method used to run the normal Template Attack.

        :param num_points_of_interest: number of points of interest to use
        :param spacing_points_of_interest: spacing between the points of interest
        :param selected_subkey: the subkey index to analyze. Must be in the range [0-16], 16 signals the whole key.
        :param bar: the progressbar to update
        :param gpu: whether or not to use gpu
        :param leakage_model: the leakage model to use
        :param debug_mode_enabled: whether to enable debug mode
        :return: list containing the calculated (sub)key
        """

        print('Template attack is being executed...')

        self.log_handler = LogHandler('ta', debug_mode_enabled)
        self.log_handler.log_to_debug_file('RUNNING template attack with \n'
                                           'TEMPLATE_TRACES: {} \n'
                                           'NUM_FEATURES: {} \n'
                                           'ATTACK_TRACES: {}\n'
                                           'SUBKEY: {}.'.format(
                                               len(self.template_traces),
                                               num_points_of_interest,
                                               len(self.attack_traces),
                                               selected_subkey))

        num_attack_traces = len(self.attack_traces)

        if selected_subkey == 16:
            return [
                self.normal_solve_subkey(x, num_points_of_interest,
                                         spacing_points_of_interest,
                                         num_attack_traces, bar, gpu,
                                         leakage_model) for x in range(16)
            ]
        else:
            result = [0 for _ in range(16)]
            result[selected_subkey] = self.normal_solve_subkey(
                selected_subkey, num_points_of_interest,
                spacing_points_of_interest, num_attack_traces, bar, gpu,
                leakage_model)
            return result
コード例 #5
0
    def __init__(self, traces: np.ndarray, keys: np.ndarray,
                 plain: np.ndarray):
        """ Constructor for mia class

        :param traces: the traces to use
        :param keys: the keys to use
        :param plain: the plaintexts to use
        """
        self.traces = traces
        self.plain = plain
        self.keys = keys

        self.NUMBER_OF_TRACES = self.traces.shape[0]
        self.RANGE = self.traces.shape[1]

        self.log_handler = LogHandler("mia", False)
コード例 #6
0
    def __init__(self, traces: np.ndarray, keys: np.ndarray, plain: np.ndarray,
                 online: bool):
        """ Constructor for mia class

        :param traces: the traces to use
        :param keys: the keys to use
        :param plain: the plaintexts to use
        :param online: use online correlation calculation
        """
        self.traces = traces
        self.plain = plain
        self.keys = keys
        self.online = online
        self.NUMBER_OF_TRACES = self.traces.shape[0]
        self.RANGE = self.traces.shape[1]

        self.log_handler = LogHandler("mia", False)
コード例 #7
0
    def test_solve_single_subkey(self):
        """Tests whether the solve_subkey method returns correctly while using the hamming_weight leakage model"""
        traces = np.load('data/traces.npy')
        keys = np.load('data/key.npy')
        plain = np.load('data/plain.npy')

        sa_obj = sa.SA(traces[:4000], keys[:4000], plain[:4000],
                       traces[4000:4030], keys[4000:4030], plain[4000:4030])
        sa_obj.log_handler = LogHandler('test_sa', False)
        bar = progressbar.NullBar()

        # In our testing set 126 is the second subkey.
        self.assertEqual(
            sa_obj.solve_subkey(1,
                                False,
                                bar,
                                1,
                                15,
                                num_traces=4000,
                                hamming_weight=True), 126)
コード例 #8
0
class TA:
    """" This class contains methods for performing a Template Attack, pooled or normal."""

    hw = [bin(x).count('1') for x in range(256)]
    log_handler: LogHandler = None

    template_traces: np.ndarray
    template_keys: np.ndarray
    template_plain: np.ndarray
    attack_traces: np.ndarray
    attack_keys: np.ndarray
    attack_plain: np.ndarray

    def __init__(self, template_traces: np.ndarray, template_keys: np.ndarray,
                 template_plaintext: np.ndarray, attack_traces: np.ndarray,
                 attack_keys: np.ndarray, attack_plaintext: np.ndarray):
        self.template_traces = template_traces
        self.template_keys = template_keys
        self.template_plain = template_plaintext
        self.attack_traces = attack_traces
        self.attack_keys = attack_keys
        self.attack_plain = attack_plaintext

    @staticmethod
    def run(template_traces: np.ndarray,
            template_keys: np.ndarray,
            template_plaintext: np.ndarray,
            attack_traces: np.ndarray,
            attack_keys: np.ndarray,
            attack_plain: np.ndarray,
            pooled: bool,
            num_points_of_interest: int,
            spacing_points_of_interest: int,
            subkey: int,
            gpu: bool = False,
            leakage_model: bool = True,
            debug_mode_enabled: bool = False,
            feature_select: int = 0) -> np.array:
        """ Method used to select correct version of Template Attack.

        :param template_traces: the traces to use
        :param template_keys: the keys to use
        :param template_plaintext: the plaintexts to use
        :param attack_traces: the traces to use for attacking
        :param attack_keys: the keys to use for attacking
        :param attack_plain: the plaintexts to use for attacking
        :param pooled: whether to do a pooled attack
        :param num_points_of_interest: number of points of interest to use
        :param spacing_points_of_interest: spacing between the points of interest
        :param subkey: the subkey index to analyze. Must be in the range [0-16]. 16 signals the full key.
        :param gpu: whether or not to use gpu for this attack
        :param leakage_model: the leakage model to use
        :param debug_mode_enabled: whether to enable debug mode
        :param feature_select: which feature selection method to use, see main for which number is which.
        :return: array containing the calculated key
        """

        # Init progress bar with rough amount of iterations
        num_subkeys = 16 if subkey == 16 else 1  # Attack takes roughly equal time per subkey so * 16 for whole key
        max_value = len(attack_traces) * 256 * num_subkeys
        bar = progressbar.ProgressBar(
            max_value=max_value,
            widgets=progress_bar_util.get_widgets(debug_mode_enabled))

        ta = TA(template_traces, template_keys, template_plaintext,
                attack_traces, attack_keys, attack_plain)

        indices = []

        if feature_select > 0:

            print("Feature selection is being calculated...")

            if num_subkeys == 16:
                for i in range(16):

                    temp = DataPartitioner.select_features(
                        template_traces, template_keys, template_plaintext, i,
                        feature_select, num_points_of_interest, 1, 0,
                        leakage_model)
                    for j in temp:
                        indices.append(j)
            else:
                # Select at least 10 features
                num_features = max(num_points_of_interest, 10)
                indices = DataPartitioner.select_features(
                    template_traces, template_keys, template_plaintext, subkey,
                    feature_select, num_features, 1, 0, leakage_model)

            ta.template_traces, ta.attack_traces = template_traces[:,
                                                                   indices], attack_traces[:,
                                                                                           indices]

        if pooled:
            result = ta.run_pooled(num_points_of_interest,
                                   spacing_points_of_interest, subkey, bar,
                                   gpu, leakage_model, debug_mode_enabled)
        else:
            result = ta.run_normal(num_points_of_interest,
                                   spacing_points_of_interest, subkey, bar,
                                   gpu, leakage_model, debug_mode_enabled)

        bar.finish()

        print('The final key is: ', result)
        return result

    def run_normal(self,
                   num_points_of_interest: int,
                   spacing_points_of_interest: int,
                   selected_subkey: int,
                   bar: progressbar.ProgressBar,
                   gpu: bool,
                   leakage_model: bool,
                   debug_mode_enabled: bool = False) -> List[int]:
        """ Run method used to run the normal Template Attack.

        :param num_points_of_interest: number of points of interest to use
        :param spacing_points_of_interest: spacing between the points of interest
        :param selected_subkey: the subkey index to analyze. Must be in the range [0-16], 16 signals the whole key.
        :param bar: the progressbar to update
        :param gpu: whether or not to use gpu
        :param leakage_model: the leakage model to use
        :param debug_mode_enabled: whether to enable debug mode
        :return: list containing the calculated (sub)key
        """

        print('Template attack is being executed...')

        self.log_handler = LogHandler('ta', debug_mode_enabled)
        self.log_handler.log_to_debug_file('RUNNING template attack with \n'
                                           'TEMPLATE_TRACES: {} \n'
                                           'NUM_FEATURES: {} \n'
                                           'ATTACK_TRACES: {}\n'
                                           'SUBKEY: {}.'.format(
                                               len(self.template_traces),
                                               num_points_of_interest,
                                               len(self.attack_traces),
                                               selected_subkey))

        num_attack_traces = len(self.attack_traces)

        if selected_subkey == 16:
            return [
                self.normal_solve_subkey(x, num_points_of_interest,
                                         spacing_points_of_interest,
                                         num_attack_traces, bar, gpu,
                                         leakage_model) for x in range(16)
            ]
        else:
            result = [0 for _ in range(16)]
            result[selected_subkey] = self.normal_solve_subkey(
                selected_subkey, num_points_of_interest,
                spacing_points_of_interest, num_attack_traces, bar, gpu,
                leakage_model)
            return result

    def run_pooled(self,
                   num_points_of_interest: int,
                   spacing_points_of_interest: int,
                   selected_subkey: int,
                   bar: progressbar.ProgressBar,
                   gpu: bool,
                   leakage_model: bool,
                   debug_mode_enabled: bool = False) -> List[int]:
        """ Run method used to run the pooled Template Attack.

        :param num_points_of_interest: number of points of interest to use
        :param spacing_points_of_interest: spacing between the points of interest
        :param selected_subkey: the subkey index to analyze. Must be in the range [0-16]
        :param bar: the progressbar to update
        :param gpu: whether or not to use gpu for this attack
        :param leakage_model: the leakage model to use
        :param debug_mode_enabled: whether to enable debug mode
        :return: list containing the calculated (sub)key
        """

        print('Pooled template attack is being executed...')

        self.log_handler = LogHandler('pooled_ta', debug_mode_enabled)

        self.log_handler.log_to_debug_file(
            'RUNNING pooled template attack with \n'
            'TEMPLATE_TRACES: {} \n'
            'NUM_FEATURES: {} \n'
            'ATTACK_TRACES: {} \n'
            'SUBKEY: {}.'.format(len(self.template_traces),
                                 num_points_of_interest,
                                 len(self.attack_traces), selected_subkey))

        num_attack_traces = len(self.attack_traces)
        result = [0 for _ in range(16)]

        mean_list, poi_list, pooled_covariance_matrix = \
            self.pooled_covariance_matrix(num_points_of_interest, spacing_points_of_interest, gpu, leakage_model)

        if selected_subkey == 16:
            for x in range(16):
                template_means = mean_list[x]
                points_of_interest = poi_list[x]

                subkey_result = self.pooled_solve_subkey(
                    x, points_of_interest, template_means,
                    num_points_of_interest, num_attack_traces,
                    pooled_covariance_matrix, bar)

                result[x] = subkey_result
        else:
            template_means = mean_list[selected_subkey]
            points_of_interest = poi_list[selected_subkey]

            result[selected_subkey] = self.pooled_solve_subkey(
                selected_subkey, points_of_interest, template_means,
                num_points_of_interest, num_attack_traces,
                pooled_covariance_matrix, bar)

        return result

    def normal_solve_subkey(self, subkey: int, num_poi: int, spacing_poi: int,
                            num_attack_traces: int,
                            bar: progressbar.ProgressBar, gpu: bool,
                            leakage_model: bool) -> int:
        """ Calls all methods needed to calculate subkey for specified cypher text

        :param subkey: the subkey index to analyze. Must be in the range [0-15]
        :param num_poi: number of points of interest to use
        :param spacing_poi: spacing between the points of interest
        :param num_attack_traces: number of data points to attack
        :param bar: the progressbar to update
        :param gpu: flag used to enable gpu.
        :param leakage_model: the leakage model to use
        :return: the calculated subkey
        """
        # Calculate all important data for the attack.
        template_traces_hamming_weight = self.calc_traces_hamming_weight(
            subkey, leakage_model, num_poi)
        template_means = self.find_average(template_traces_hamming_weight, gpu)
        template_sum_difference = self.find_sum_of_differences(template_means)
        points_of_interest = self.find_points_of_interest(
            num_poi, spacing_poi, template_sum_difference)
        mean_matrix = self.calc_mean_matrix(num_poi, template_means,
                                            points_of_interest)
        covariance_matrix = self.calc_covariance_matrix(
            num_poi, template_traces_hamming_weight, points_of_interest)

        # Execute the attack.
        return self.execute_attack(subkey, points_of_interest, mean_matrix,
                                   covariance_matrix, num_attack_traces, bar)

    def calc_traces_hamming_weight(
            self,
            subkey: int,
            leakage_model: bool,
            min_points_of_interest: int = 2) -> List[np.ndarray]:
        """
        Method that calculates intermediate sbox and hamming weight traces.

        :param subkey: the subkey index to analyze. Must be in the range [0-15]
        :param leakage_model: the leakage model to use
        :param min_points_of_interest: minimum amount of points required per class.
        :return: list containing matrix per hamming weight
        """

        # Create the right hamming weight structure.
        template_hamming_weight = HammingWeight.hamming_weights(
            self.template_plain,
            self.template_keys,
            subkey,
            hamming_leakage=leakage_model)

        if leakage_model:
            hamming_weight_size = 9
        else:
            hamming_weight_size = 256

        # Put each trace in the right category and change into NumPy array.
        template_traces_hamming_weight = [[]
                                          for _ in range(hamming_weight_size)]

        for i in range(len(self.template_traces)):
            temp = template_hamming_weight[i]
            template_traces_hamming_weight[temp].append(
                self.template_traces[i])

        template_traces_hamming_weight = [
            np.array(template_traces_hamming_weight[i])
            for i in range(hamming_weight_size)
        ]

        min_length = min_points_of_interest
        for i in range(len(template_traces_hamming_weight)):
            if len(template_traces_hamming_weight[i]) < min_length:
                min_length = len(template_traces_hamming_weight[i])

        if min_length < min_points_of_interest:
            print(
                'ERROR: Not enough values of certain class. Maximum amount of points of interest for this '
                'dataset is %d' % min_length)
            exit(1)
        return template_traces_hamming_weight

    def find_average(self, template_traces_hamming_weight: List[np.ndarray],
                     gpu: bool) -> np.ndarray:
        """ Method for finding the averages of the traces.

        :param template_traces_hamming_weight: list containing matrix per hamming weight
        :param gpu: whether or not to use gpu for this operation
        :return: matrix containing averages for the traces given the hamming weights
        """

        if gpu:
            # Find an average trace for each weight.
            template_means = torch.zeros((len(template_traces_hamming_weight),
                                          len(self.template_traces[0])))
            tthw_t = []
            for i in range(len(template_traces_hamming_weight)):
                tthw_t.append(
                    torch.as_tensor(template_traces_hamming_weight[i]).cuda())
            for i in range(len(template_traces_hamming_weight)):
                template_means[i] = torch.mean(tthw_t[i], dim=0)

            return template_means.cpu().numpy()

        else:
            # Find an average trace for each weight.
            template_means = np.zeros((len(template_traces_hamming_weight),
                                       len(self.template_traces[0])))
            for i in range(len(template_traces_hamming_weight)):
                template_means[i] = np.average(
                    template_traces_hamming_weight[i], 0)

        return template_means

    def find_sum_of_differences(self,
                                template_means: np.ndarray) -> np.ndarray:
        """ Method that find the sum of the differences of the traces.

        :param template_means: the means of the traces
        :return: matrix containing sum of differences of the traces
        """

        # Use the sum of differences method to find points of interest.
        template_sum_difference = np.zeros(len(self.template_traces[0]))
        for i in range(len(template_means)):
            for j in range(i):
                template_sum_difference += np.abs(template_means[i] -
                                                  template_means[j])

        return template_sum_difference

    @staticmethod
    def find_points_of_interest(
            num_poi: int, spacing_poi: int,
            template_sum_difference: np.ndarray) -> List[int]:
        """ Method for finding the points of interest of the traces.

        :param num_poi: number of points of interest to use
        :param spacing_poi: spacing between the points of interest
        :param template_sum_difference: matrix containing sum of differences of the traces
        :return: list containing indices of points of interest
        """

        points_of_interest = []

        for i in range(num_poi):
            # Add the biggest sum of difference to the points of interest.
            next_point_of_interest = template_sum_difference.argmax()
            points_of_interest.append(next_point_of_interest)

            # Make sure the next point of interest is the only peak in its spacing.
            min_point_of_interest = max(0,
                                        next_point_of_interest - spacing_poi)
            max_point_of_interest = min(next_point_of_interest + spacing_poi,
                                        len(template_sum_difference))
            for j in range(min_point_of_interest, max_point_of_interest):
                template_sum_difference[j] = 0

        return points_of_interest

    @staticmethod
    def calc_mean_matrix(num_points_of_interest: int,
                         template_means: np.ndarray,
                         points_of_interest: List[int]):
        """ Method that calculates the mean matrix used in the Template Attack.

        :param num_points_of_interest: number of points of interest to use
        :param template_means: the means of the traces
        :param points_of_interest: list containing indices of points of interest
        :return: mean matrix
        """

        mean_matrix = np.zeros((len(template_means), num_points_of_interest))
        for HW in range(len(template_means)):
            for i in range(num_points_of_interest):
                mean_matrix[HW][i] = template_means[HW][int(
                    points_of_interest[i])]

        return mean_matrix

    @staticmethod
    def calc_covariance_matrix(
            num_points_of_interest: int,
            template_traces_hamming_weight: List[np.ndarray],
            points_of_interest: List[int]):
        """ Method that calculates the covariance matrix used in the Template Attack.

        :param num_points_of_interest: number of points of interest to use
        :param template_traces_hamming_weight: list containing matrix per hamming weight
        :param points_of_interest: list containing indices of points of interest
        :return: covariance matrix
        """

        covariance_matrix = np.zeros(
            (len(template_traces_hamming_weight), num_points_of_interest,
             num_points_of_interest))
        for hamming_weight in range(len(template_traces_hamming_weight)):
            for i in range(num_points_of_interest):
                for j in range(num_points_of_interest):
                    x = template_traces_hamming_weight[
                        hamming_weight][:, points_of_interest[i]]
                    y = template_traces_hamming_weight[
                        hamming_weight][:, points_of_interest[j]]
                    covariance_matrix[hamming_weight, i, j] = np.cov(x,
                                                                     y)[0][1]

        return covariance_matrix

    def execute_attack(self, subkey: int, points_of_interest: List[int],
                       mean_matrix: np.ndarray, covariance_matrix: np.ndarray,
                       num_attack_traces: int,
                       bar: progressbar.ProgressBar) -> int:
        """ Method that executes the normal Template Attack after all necessary data has been found and calculated.

        :param subkey: the subkey index to analyze. Must be in the range [0-15]
        :param points_of_interest: points of interest to use
        :param mean_matrix: mean_matrix to use
        :param covariance_matrix: covariance_matrix to use
        :param num_attack_traces: number of data points to attack
        :param bar: the progressbar to update
        :return: the calculated subkey
        """

        # Log the points of interest and mean matrix whilst debugging
        self.log_handler.log_to_debug_file("POI indices for subkey " +
                                           str(subkey) + ": " +
                                           str(points_of_interest) + "\n")
        self.log_handler.log_to_debug_file("Template Means for subkey " +
                                           str(subkey) + ":\n" +
                                           np.array2string(mean_matrix) + "\n")

        key_guesses = np.zeros(256)

        for j in range(num_attack_traces):
            # Grab key points and put them in a matrix
            a = [
                self.attack_traces[j][int(points_of_interest[i])]
                for i in range(len(points_of_interest))
            ]

            # Test each key
            for k in range(256):
                # Find HW coming out of sbox
                hamming_weight = self.hw[aes.sbox[self.attack_plain[j][subkey]
                                                  ^ k]]

                # Find p_{k,j}
                rv = multivariate_normal(mean_matrix[hamming_weight],
                                         covariance_matrix[hamming_weight])
                p_kj = rv.pdf(a)

                # Add it to running totalSS
                if p_kj != 0:
                    key_guesses[k] += np.log(p_kj)

                bar.update(bar.value + 1)

        # Print the top 5 subkeys whilst we are loghandler
        self.log_handler.log_to_debug_file(
            self.tabulate_top_n(key_guesses, 5, subkey))

        subkey_result = int(key_guesses.argsort()[-1:])

        # Print the subkey while debugging
        debug_string = \
            "Subkey " + str(subkey) + " is: " + str(subkey_result) + " | POI indices: " + str(points_of_interest)
        self.log_handler.log_to_debug_progressbar(bar, debug_string)
        self.log_handler.log_to_debug_file(self.log_handler.CONST_SEPARATOR)

        return subkey_result

    @staticmethod
    def tabulate_top_n(data: np.ndarray, n: int, subkey: int) -> str:
        """"" This method prints the most occurring key guesses

        :param data: key_guesses of the template attack
        :param n: amount of key guesses to log
        :param subkey: the subkey index which has been analyzed
        :return: log string
        """

        maximum = np.max(data)
        data /= maximum
        parsed_data = [(x, data[x]) for x in reversed(np.argsort(data)[-n:])]
        return tabulate(parsed_data,
                        headers=['Subkey: ' + str(subkey), 'Relative score'])

    def pooled_solve_subkey(self, subkey: int, points_of_interest: List[int],
                            template_means: np.ndarray, num_poi: int,
                            num_attack_traces: int,
                            pooled_covariance_matrix: np.ndarray,
                            bar: progressbar.ProgressBar):
        """ Calls all methods needed to calculate subkey for specified cypher text

        :param subkey: the subkey index to analyze. Must be in the range [0-15]
        :param points_of_interest: list containing indices of points of interest
        :param template_means: the means of the traces
        :param num_poi: number of points of interest to use
        :param num_attack_traces: number of data points to attack
        :param pooled_covariance_matrix: covariance_matrix to use
        :param bar: the progressbar to update
        :return: the calculated subkey
        """
        # Calculate all important data for the attack, most of it is already calculated whilst calculating the pooled
        # covariance matrix, thus we pass that information on and don't calculate it again.

        mean_matrix = self.calc_mean_matrix(num_poi, template_means,
                                            points_of_interest)

        # Execute the attack.
        return self.execute_attack(subkey, points_of_interest, mean_matrix,
                                   pooled_covariance_matrix, num_attack_traces,
                                   bar)

    def pooled_covariance_matrix(self, num_poi: int, spacing_poi: int,
                                 gpu: bool, leakage_model: bool):
        """ Calculates the pooled covariance matrix and stores all needed intermediate results for future use

        :param num_poi: number of points of interest to use
        :param spacing_poi: spacing between the points of interest
        :param gpu: whether to use GPU for matrix computations
        :param leakage_model: the leakage model to use
        :return: pooled covariance matrix
        """

        if leakage_model:
            length_leakage_model = 9
        else:
            length_leakage_model = 256

        mean_list = np.zeros(
            (16, length_leakage_model, self.template_traces.shape[1]))
        poi_list = np.zeros((16, num_poi))
        covariance_matrices = np.zeros(
            (16, length_leakage_model, num_poi, num_poi))

        for subkey in range(16):
            template_traces_hamming_weight = self.calc_traces_hamming_weight(
                subkey, leakage_model, num_poi)
            template_means = self.find_average(template_traces_hamming_weight,
                                               gpu)
            template_sum_difference = self.find_sum_of_differences(
                template_means)
            points_of_interest = self.find_points_of_interest(
                num_poi, spacing_poi, template_sum_difference)
            covariance_matrix = self.calc_covariance_matrix(
                num_poi, template_traces_hamming_weight, points_of_interest)

            mean_list[subkey] = template_means
            poi_list[subkey] = points_of_interest
            covariance_matrices[subkey] = covariance_matrix

        if gpu:
            t_c = torch.as_tensor(np.array([i for i in covariance_matrices
                                            ])).cuda()
            pooled_covariance_matrix = torch.mean(t_c, dim=0).cpu()
        else:
            pooled_covariance_matrix = np.mean(np.array(
                [i for i in covariance_matrices]),
                                               axis=0)

        return mean_list, poi_list, pooled_covariance_matrix
コード例 #9
0
    def solve_subkey(self,
                     subkey: int,
                     hamming_leakage: bool = True,
                     debug_mode_enabled: bool = False,
                     round_: int = 1,
                     operation: int = 0,
                     bar: progressbar.ProgressBar = None) -> int:
        """ Solve a subkey
        :param subkey: the subkey index to analyze. Must be in the range [0-15]
        :param hamming_leakage: whether to use the hamming_weight leakage model
        :param debug_mode_enabled: whether to enable debug mode
        :param round_: round of aes.
        :param operation: operation of aes.
        :param bar: the progressbar to update
        :return: the calculated subkey corresponding to the subkey index specified
        """

        log_handler = LogHandler("mia", debug_mode_enabled)

        log_handler.log_to_debug_file('RUNNING mia attack with \n'
                                      'NUM_FEATURES: {} \n'
                                      'ATTACK_TRACES: {} \n'
                                      'SUBKEY: {}.'.format(
                                          self.RANGE, self.NUMBER_OF_TRACES,
                                          subkey))

        self.calculate_leakage_table(subkey, bar, round_, operation,
                                     hamming_leakage)

        pool = multiprocessing.Pool(multiprocessing.cpu_count())
        result = []
        calculate_point_in_time_subkey = partial(self.calculate_point_in_time)
        best_key = -1
        best_mutual_information = 0
        best_time = -1

        for i, value in enumerate(
                pool.map(calculate_point_in_time_subkey,
                         range(self.OFFSET, self.OFFSET + self.RANGE)), 2):
            mutual_information, key = value

            # Update the most probable key guess
            if mutual_information > best_mutual_information:
                best_mutual_information = mutual_information
                best_key = key
                best_time = i

            # Print and log the necessary stuff while debugging
            string = "MI at: t=%d is %f with key guess: %d | Best so far: t=%d, MI=%f, key=%d"
            data = (i, mutual_information, int(key), best_time,
                    best_mutual_information, int(best_key))
            debug_string = string % data

            log_handler.log_to_debug_file(debug_string)
            log_handler.log_to_debug_progressbar(bar, debug_string)
            if bar is not None and bar.value < bar.max_value:
                bar.update(bar.value + 1)
            result.append(value)

        pool.close()

        log_handler.log_to_debug_file(self.tabulate_top_n(result, 10))

        return best_key
コード例 #10
0
    def run(profiling_traces: np.ndarray, profiling_keys: np.ndarray, profiling_plaintext: np.ndarray,
            attack_traces: np.ndarray, attack_keys: np.ndarray, attack_plaintext: np.ndarray, round_: int,
            operation: int, num_traces: int, num_attack_traces: int, subkey: int, feature_select: int,
            num_features: int, use_gpu: bool = False, hamming_weight: bool = False, debug_mode_enabled: bool = False) \
            -> List[int]:
        """
        Runs a full stochastic attack

        :param profiling_traces: the traces to use
        :param profiling_keys: the keys to use
        :param profiling_plaintext: the plaintexts to use
        :param attack_traces: the traces to use for attacking
        :param attack_keys: the keys to use for attacking
        :param attack_plaintext: the plaintexts to use for attacking
        :param round_: the AES round to attack
        :param operation: the AES operation to attack, represented as an integer from 0 to 3
        :param num_traces: number of data points to process
        :param num_attack_traces: number of data points to attack
        :param feature_select: which feature select method to use.
        :param num_features: the number of features to select
        :param subkey: the subkey index to analyze. Must be in the range [0-16], 16 signaling the whole key.
        :param use_gpu: whether or not to use gpu acceleration
        :param hamming_weight: whether or not to use the hamming_weight leakage model
        :param debug_mode_enabled: whether or not to enable debug mode
        :returns: list containing the (sub)key
        """

        print('This performs a stochastic attack.')

        # Instantiate a SA (Stochastic Attack) object to call dynamic method.
        sa = SA(profiling_traces, profiling_keys, profiling_plaintext,
                attack_traces, attack_keys, attack_plaintext)
        sa.log_handler = LogHandler('sa', debug_mode_enabled)

        if sa.log_handler is not None:
            sa.log_handler.log_to_debug_file(
                'RUNNING stochastic attack with \n'
                'TEMPLATE_TRACES: {} \n'
                'NUM_FEATURES: {} \n'
                'ATTACK_TRACES: {} \n'
                'SUBKEY: {}.'.format(len(sa.profiling_traces), num_features,
                                     len(sa.attack_traces), subkey))

        # Init progress bar with estimated amount of iterations
        num_subkeys = 16 if subkey == 16 else 1
        max_value = num_attack_traces * 256 * num_subkeys * 15
        bar = progressbar.ProgressBar(
            max_value=max_value,
            widgets=progress_bar_util.get_widgets(debug_mode_enabled))

        result = [0 for _ in range(16)]

        # call method that returns the key, currently with no arguments taken from the user.
        if subkey == 16:
            for i in range(subkey):
                result[i] = sa.solve_subkey(
                    i,
                    use_gpu,
                    bar,
                    feature_select=feature_select,
                    num_features=num_features,
                    num_traces=num_traces,
                    num_attack_traces=num_attack_traces,
                    hamming_weight=hamming_weight,
                    aes_round=round_,
                    aes_operation=operation)
        else:
            result[subkey] = sa.solve_subkey(
                subkey,
                use_gpu,
                bar,
                feature_select=feature_select,
                num_features=num_features,
                num_traces=num_traces,
                num_attack_traces=num_attack_traces,
                hamming_weight=hamming_weight,
                aes_round=round_,
                aes_operation=operation)

        bar.finish()

        print('The final key is: ', result)
        return result