Ejemplo n.º 1
0
    def list_of_best_indices(traces: np.ndarray,
                             keys: np.ndarray,
                             plain: np.ndarray,
                             points_of_interest: int,
                             subkey: int,
                             aes_round: int = 1,
                             aes_operation: int = 0,
                             hamming_weight: bool = True,
                             normalization: bool = True):
        """
        This method performs the full SOST or SOSD analysis.

        :param traces: the traces used for the tool
        :param keys: the keys used for the tool
        :param plain: the plain used for the tool
        :param points_of_interest: the number of points of interest to extract.
        :param subkey: the subkey to correlate on. Used for hamming weight
            calculation, must be in the range [0-15].
        :param aes_round: the AES round to 'attack'.
        :param hamming_weight: whether to use the hamming_weight leakage model
        :param aes_operation: the AES operation to 'attack', represented as an integer from 0 to 3.
        :param normalization: whether to normalize the coefficients or not. Normalization is part of SOST, without it,
            SOSD is run.
        :return List of best n indices
        """
        hamming_weights = HammingWeight.hamming_weights(
            plain, keys, subkey, aes_round, aes_operation, hamming_weight)
        indices = SOSTD.list_of_indices_per_value(hamming_weights, traces,
                                                  hamming_weight)
        sostd_coefficients = SOSTD.sostd_coefficients(indices, traces,
                                                      hamming_weight,
                                                      normalization)

        return np.argsort(-sostd_coefficients)[:points_of_interest][::-1]
    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)
    def calculate_mean_x_given_y_matrix(plain_texts: np.ndarray,
                                        traces: np.ndarray,
                                        sub_byte_index_to_analyze: int,
                                        keys: np.ndarray,
                                        hamming_weight: bool = True,
                                        aes_round: int = 1,
                                        aes_operation: int = 0) -> np.ndarray:
        """Mean_y_given_x means that we calculate for every possible sub byte x
        and a given timestamp the mean of y at that point
        x specifies a sub_byte in the plain_text, y specifies an output trace

        :param plain_texts: the plaintexts to use
        :param traces: the traces to use
        :param keys: the keys to use
        :param sub_byte_index_to_analyze: the subkey index to analyze. Must be in the range [0-15]
        :param hamming_weight: whether to use the hamming_weight leakage model
        :param aes_round; the round of aes to analyze
        :param aes_operation: the operation of aes to analyze
        :return: a numpy matrix containing means for every sub byte
        """

        length_trace = len(traces[0])
        hamming_weights = HammingWeight.hamming_weights(
            plain_texts, keys, sub_byte_index_to_analyze, aes_round,
            aes_operation, hamming_weight)
        if hamming_weight:
            amount_of_values = 9
        else:
            amount_of_values = 256

        mean_y_given_x_matrix = np.empty((amount_of_values, length_trace))

        for i in range(amount_of_values):

            traces_with_x = []

            # Filter out the traces which contain the current sub byte
            for j in range(len(plain_texts)):
                if hamming_weights[j] == i:
                    traces_with_x.append(j)

            # Now calculate the mean of this specific x for every given timestamp
            for j in range(length_trace):
                list_values = np.empty((len(traces_with_x)))
                for k in range(len(traces_with_x)):
                    list_values[k] = traces[traces_with_x[k]][j]
                if len(list_values) > 0:
                    mean_y_given_x_matrix[i][j] = np.mean(list_values)
                # If there are no traces which contain the following x set the mean at 0, this will not happen
                # if the trace size is much bigger than 255
                # TODO: maybe find a more sophisticated solution to this problem
                else:
                    mean_y_given_x_matrix[i][j] = 0

        return mean_y_given_x_matrix
    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)
Ejemplo n.º 5
0
    def test_calculate_hamming_weights(self):
        """Test that the calculated hamming weights are correct."""

        plaintext = np.array([
            [
                253, 175, 146, 139, 39, 24, 40, 196, 38, 225, 26, 31, 238, 15,
                0, 30
            ],
            [
                162, 7, 17, 31, 86, 200, 98, 60, 166, 235, 184, 253, 211, 48,
                91, 149
            ],
            [
                16, 181, 1, 53, 76, 95, 58, 44, 17, 219, 19, 230, 141, 133, 68,
                136
            ],
            [
                48, 0, 86, 35, 156, 15, 97, 135, 201, 46, 209, 49, 238, 12,
                197, 127
            ],
            [
                14, 44, 55, 24, 226, 225, 128, 116, 29, 129, 98, 180, 185, 188,
                241, 254
            ],
        ])
        key = np.array([
            [
                34, 123, 137, 185, 176, 241, 60, 235, 195, 90, 64, 204, 173,
                62, 49, 13
            ],
            [
                5, 203, 149, 118, 67, 107, 181, 88, 186, 63, 9, 246, 52, 7,
                144, 59
            ],
            [
                243, 98, 136, 17, 128, 170, 12, 18, 143, 62, 83, 49, 244, 204,
                247, 192
            ],
            [
                152, 77, 141, 53, 182, 175, 145, 242, 232, 203, 104, 91, 5,
                174, 206, 126
            ],
            [
                0, 236, 171, 201, 204, 216, 94, 50, 233, 124, 230, 23, 13, 100,
                65, 150
            ],
        ])

        hamming_weights = HammingWeight.hamming_weights(plaintext, key, 0)

        self.assertTrue(np.array_equal(hamming_weights, [5, 4, 2, 3, 5]))
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    def run(traces: np.ndarray,
            keys: np.ndarray,
            plain: np.ndarray,
            points_of_interest: int,
            save_traces: bool,
            subkey: int,
            aes_round: int = 1,
            aes_operation: int = 0,
            leakage_model: bool = True):
        """
        This method performs the full Pearson correlation analysis.

        :param traces: the traces used for the tool
        :param keys: the keys used for the tool
        :param plain: the plain used for the tool
        :param points_of_interest: the number of points of interest to extract.
        :param save_traces: whether to save the full traces or just their indices.
        :param subkey: the subkey to correlate on. Used for hamming weight
            calculation, must be in the range [0-15].
        :param aes_round: the AES round to 'attack'.
        :param aes_operation: the AES operation to 'attack', represented as an integer from 0 to 3.
        :param leakage_model: the leakage model to use.
        """

        assert 0 <= subkey < 16, "Subkey must be in the range [0-15]"

        hamming_weights = HammingWeight.hamming_weights(
            plain, keys, subkey, aes_round, aes_operation, leakage_model)
        indices = Pearson.pearson_coefficients(hamming_weights,
                                               points_of_interest, traces)

        if save_traces:
            print('Saving interesting traces... ', end='')
            np.save('out/pearson_correlation_selected_traces', traces[:,
                                                                      indices])
        else:
            print('Saving indices of interesting traces... ', end='')
            np.save('out/pearson_correlation_selected_indices', indices)

        print('Done')
Ejemplo n.º 8
0
    def best_indices(traces: np.ndarray,
                     keys: np.ndarray,
                     plain: np.ndarray,
                     points_of_interest: int,
                     subkey: int,
                     aes_round: int = 1,
                     aes_operation: int = 0,
                     leakage_model: bool = True):
        """
        This method performs the full Pearson correlation analysis.

        :param traces: the traces used for the tool
        :param keys: the keys used for the tool
        :param plain: the plain used for the tool
        :param points_of_interest: the number of points of interest to extract.
        :param subkey: the subkey to correlate on. Used for hamming weight
            calculation, must be in the range [0-15].
        :param aes_round: the AES round to 'attack'.
        :param aes_operation: the AES operation to 'attack', represented as an integer from 0 to 3.
        :param leakage_model: whether to use hamming weight leakage model
        """

        hamming_weights = HammingWeight.hamming_weights(
            plain, keys, subkey, aes_round, aes_operation, leakage_model)
        if leakage_model:
            counts = np.zeros(9)
        else:
            counts = np.zeros(256)
        for i in hamming_weights:
            counts[i] += 1

        for i in counts:
            if i < 2:
                print(
                    'WARNING: Pearson does not have enough data points of certain value, '
                    'results may not be accurate.')
                return Pearson.pearson_coefficients(hamming_weights,
                                                    points_of_interest, traces)
        return Pearson.pearson_coefficients(hamming_weights,
                                            points_of_interest, traces)
    def calculate_function_matrix(self, subkey: int):
        """
        Applies model functions to the traces and plains per possible subkey hypothesis byte
        current 10 model functions:
            functions m0 till m7: returns the i'th bith of the input
            hamming_weight m8: returns the hamming weights for every subkey hypothesis
            basis function m9: just returns 1 for every leakage

        :param: the subkey index to calculate
        :return: matrix containing model functions applied to each possible subkey and all traces
        """

        functions_per_byte_value_matrix = np.zeros(
            (self.KEY_SIZE, self.number_of_traces, 10))

        for subkey_hypothesis in range(self.KEY_SIZE):
            plain = self.plains

            keys = np.array([[subkey_hypothesis] * 16] * self.number_of_traces)
            hamming_weight_func = HammingWeight.hamming_weights(
                plain, keys, subkey)

            matrix_m = np.ones((10, self.number_of_traces))
            matrix_m[9] = hamming_weight_func

            for i in range(8):
                for j in range(self.number_of_traces):
                    xor = plain[j][subkey] ^ subkey_hypothesis
                    matrix_m[i][j] = xor >> i & 1

            matrix_m = np.swapaxes(matrix_m, 0, 1)

            functions_per_byte_value_matrix[subkey_hypothesis] = matrix_m

            self.bar.update(self.bar.value + 1)

        return functions_per_byte_value_matrix
Ejemplo n.º 10
0
    def test_leakage_func(self):
        result = HammingWeight._hamming_leakage(
            np.array([0xFF, 0x3F, 0x2C, 0xD9]))

        print(result)
        self.assertTrue(np.array_equal(result, [8, 6, 3, 5]))
Ejemplo n.º 11
0
    def conditional_avg(self,
                        subkey: int,
                        hamming_leakage: bool,
                        round_: int = 1,
                        operation: int = 0,
                        bar: progressbar.ProgressBar = None):
        """
        Calculates coefficients using conditional averaging.

        :param subkey: the subkey to calculate.
        :param hamming_leakage: signifies which leakage model is used.
        :param round_: round of aes.
        :param operation: operation of aes.
        :param bar: the progressbar to update.
        :return: the coefficients, calculated using conditional averaging.
        """
        num_traces = len(self.traces)

        num_values = 256
        if hamming_leakage:
            num_values = 9

        results = np.zeros(self.KEY_SIZE)

        for key in range(self.KEY_SIZE):
            conditional_avg = np.zeros(num_values)
            count = np.zeros(num_values)
            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)

            var_range = np.var(leakage_table)
            mean_range = np.mean(leakage_table)
            covariance_index = np.zeros(len(self.traces[0]))

            for x in range(len(self.traces[0])):
                for i in range(num_traces):
                    conditional_avg[leakage_table[i]] = conditional_avg[
                        leakage_table[i]] + self.traces[i][x]
                    count[leakage_table[i]] = count[leakage_table[i]] + 1
                for i in range(np.power(2, num_traces)):
                    conditional_avg[i] = conditional_avg[i] / count[i]

            for x in range(len(self.traces[0])):
                mean = np.mean(conditional_avg)
                variance = np.var(conditional_avg)
                covariance = 0

                for i in range(num_values):
                    covariance = covariance + i * conditional_avg[i]

                covariance_index[x] = np.abs(covariance / num_values -
                                             mean * mean_range) / np.sqrt(
                                                 variance * var_range)

            if bar is not None and bar.value < bar.max_value:
                bar.update(bar.value + 1)

            results[key] = np.max(covariance_index)

        return np.argmax(results)