diff --git a/claasp/cipher_modules/component_analysis_tests.py b/claasp/cipher_modules/component_analysis_tests.py index e525cc0c..de312549 100644 --- a/claasp/cipher_modules/component_analysis_tests.py +++ b/claasp/cipher_modules/component_analysis_tests.py @@ -153,15 +153,19 @@ def print_component_analysis_as_radar_charts(self, results=None): plt.rcParams['figure.figsize'] = [20, 20] # remove XOR from results - results_without_xor = [results[i] for i in range(len(results)) if results[i]["description"][0] != "XOR"] - results = self._remove_components_with_strings_as_values(results_without_xor) + # results_without_xor = [results[i] for i in range(len(results)) if results[i]["description"][0] != "XOR"] + results_without_fsr = [results[i] for i in range(len(results)) if results[i]["type"] != "fsr"] + # removed for now because the fsr dictionary does not follow the standard structure as the other components: + # the keys properties, values, etc are not present. + # results = self._remove_components_with_strings_as_values(results_without_xor) + results = self._remove_components_with_strings_as_values(results_without_fsr) nb_plots = len(results) col = 2 row = nb_plots // col if nb_plots % col != 0: row += nb_plots % col - positions = {8: -0.7, 3: -0.4} + positions = {8: -0.7, 3: -0.4} # positions of the text according to the numbers of properties for plot_number in range(nb_plots): categories = list(results[plot_number]["properties"].keys()) @@ -211,10 +215,12 @@ def print_component_analysis_as_radar_charts(self, results=None): self._fill_area(ax, categories, plot_number, positions, results) # Show the graph - plt.subplots_adjust(left=0.25, bottom=0.1, right=0.7, top=0.95, wspace=0, hspace=0.96) + if nb_plots >= 5: + plt.subplots_adjust(left=0.02, bottom=0.1, right=0.7, top=0.95, wspace=1, hspace=0.96) + else: + plt.subplots_adjust(left=0.09, bottom=0.3, right=0.7, top=0.7, wspace=1, hspace=0.96) plt.show() #print("The radar chart can be plot with the build-in method plt.show()") - #return plt @@ -274,7 +280,7 @@ def _select_boolean_function(self, component, boolean_polynomial_ring): elif component.description[0] == "MODADD": return self._MODADD_as_boolean_function(component, boolean_polynomial_ring) else: - return "TODO(...)" + return f"TODO: {component.id} not implemented yet" def _MODADD_as_boolean_function(self, component, boolean_polynomial_ring): @@ -425,11 +431,8 @@ def _select_properties_function(self, boolean_polynomial_ring, operation): if component.type == 'fsr': return self._fsr_properties(operation) - if component.type == WORD_OPERATION: - print(f"TODO : {component.description[0]}") - return {} else: - print(f"TODO : {component.type}") + # print(f"TODO: not implemented yet") return {} def _is_mds(self, component): @@ -485,7 +488,6 @@ def _word_operation_properties(self, operation, boolean_polynomial_ring): INPUT: - ``operation`` -- **list**; a list containing: - * a component with the operation under study * number of occurrences of the operation * list of ids of all the components with the same underlying operation @@ -642,7 +644,7 @@ def _linear_layer_properties(self, operation): "min_possible_value": 1, "max_possible_value": pow(2, component.input_bit_size) - 1 } - if component.input_bit_size <= 32: + if component.input_bit_size <= 64: dictio["properties"]["differential_branch_number"] = {"value": branch_number(component, 'differential', 'bit'), "min_possible_value": 0, "max_possible_value": component.input_bit_size} @@ -883,7 +885,8 @@ def _fill_area(self, ax, categories, plot_number, positions, results): text += f"{category} = {int(results[plot_number]['properties'][category]['value'])} " \ f"(best is {results[plot_number]['properties'][category]['max_possible_value']}, " \ f"worst is {results[plot_number]['properties'][category]['min_possible_value']})\n" - plt.text(0, positions[len(categories)], text, transform=ax.transAxes, size="small") + # plt.text(0, positions[len(categories)], text, transform=ax.transAxes, size="small") + plt.text(2, 0, text, transform=ax.transAxes, size="small") def _initialise_spider_plot(self, plot_number, results): is_component_word_operation = results[plot_number]["type"] == "word_operation" diff --git a/claasp/cipher_modules/neural_network_tests.py b/claasp/cipher_modules/neural_network_tests.py index 61a4d9e8..719892bc 100644 --- a/claasp/cipher_modules/neural_network_tests.py +++ b/claasp/cipher_modules/neural_network_tests.py @@ -146,7 +146,7 @@ def _update_blackbox_distinguisher_vectorized_tests_ds(self, base_inputs, base_o # cipher_output = base_output base_inputs_np = [np.broadcast_to( - np.array([b for b in x.to_bytes(input_lengths[i] // 8, byteorder='big')], dtype=np.uint8), + np.array([b for b in int(x).to_bytes(input_lengths[i] // 8, byteorder='big')], dtype=np.uint8), (nb_samples, input_lengths[i] // 8) ).transpose().copy() for i, x in enumerate(base_inputs)] random_inputs_for_index = np.frombuffer(os.urandom(nb_samples * input_lengths[index] // 8), @@ -313,7 +313,7 @@ def _update_distinguisher_vectorized_tests_ds(self, base_inputs, d, ds, index, l random_labels_size = nb_samples - np.count_nonzero(np.array(labels)) base_inputs_np = [np.broadcast_to( - np.array([b for b in x.to_bytes(input_lengths[i] // 8, byteorder='big')], dtype=np.uint8), + np.array([b for b in int(x).to_bytes(input_lengths[i] // 8, byteorder='big')], dtype=np.uint8), (nb_samples, input_lengths[i] // 8) ).transpose().copy() for i, x in enumerate(base_inputs)] random_inputs_for_index = np.frombuffer(os.urandom(nb_samples * input_lengths[index] // 8), @@ -323,7 +323,7 @@ def _update_distinguisher_vectorized_tests_ds(self, base_inputs, d, ds, index, l other_inputs_np = list(base_inputs_np) - d_array = np.array([b for b in d.to_bytes(input_lengths[index] // 8, byteorder='big')]) + d_array = np.array([b for b in int(d).to_bytes(input_lengths[index] // 8, byteorder='big')]) other_inputs_np[index] = other_inputs_np[index] ^ np.broadcast_to(d_array, ( nb_samples, input_lengths[index] // 8)).transpose() diff --git a/claasp/cipher_modules/report.py b/claasp/cipher_modules/report.py index 3e7cbdca..8bf05de9 100644 --- a/claasp/cipher_modules/report.py +++ b/claasp/cipher_modules/report.py @@ -478,7 +478,7 @@ def _print_trail(self, word_size, state_size, key_state_size, verbose, show_word value = self.test_report['components_values'][comp_id]['value'] - bin_list = list(format(int(value, 16), 'b').zfill(4 * len(value)) if value[:2] != '0x' else 4 * len(value[2:])) if '*' not in value else list( + bin_list = list(format(int(value, 16), 'b').zfill(4 * len(value) if value[:2] != '0x' else 4 * len(value[2:]))) if '*' not in value else list( value[2:]) word_list = ['*' if '*' in ''.join(bin_list[x:x + word_size]) else word_denominator if ''.join(bin_list[x:x + word_size]).count('1') > 0 else '_' for x in diff --git a/tests/unit/cipher_modules/models/sat/sat_models/sat_xor_differential_model_test.py b/tests/unit/cipher_modules/models/sat/sat_models/sat_xor_differential_model_test.py index 323c63ce..a887a055 100644 --- a/tests/unit/cipher_modules/models/sat/sat_models/sat_xor_differential_model_test.py +++ b/tests/unit/cipher_modules/models/sat/sat_models/sat_xor_differential_model_test.py @@ -4,6 +4,7 @@ from claasp.cipher_modules.models.utils import set_fixed_variables, integer_to_bit_list from claasp.cipher_modules.models.sat.sat_models.sat_xor_differential_model import SatXorDifferentialModel +speck_5rounds = SpeckBlockCipher(number_of_rounds=5) def test_find_all_xor_differential_trails_with_fixed_weight(): speck = SpeckBlockCipher(number_of_rounds=5) sat = SatXorDifferentialModel(speck, window_size_weight_pr_vars=1) @@ -17,7 +18,7 @@ def test_find_all_xor_differential_trails_with_fixed_weight(): def test_find_all_xor_differential_trails_with_weight_at_most(): - speck = SpeckBlockCipher(number_of_rounds=5) + speck = speck_5rounds sat = SatXorDifferentialModel(speck) plaintext = set_fixed_variables(component_id='plaintext', constraint_type='not_equal', bit_positions=range(32), bit_values=integer_to_bit_list(0, 32, 'big')) @@ -29,7 +30,7 @@ def test_find_all_xor_differential_trails_with_weight_at_most(): def test_find_lowest_weight_xor_differential_trail(): - speck = SpeckBlockCipher(number_of_rounds=5) + speck = speck_5rounds sat = SatXorDifferentialModel(speck) plaintext = set_fixed_variables(component_id='plaintext', constraint_type='not_equal', bit_positions=range(32), bit_values=(0,) * 32) @@ -41,7 +42,7 @@ def test_find_lowest_weight_xor_differential_trail(): def test_find_one_xor_differential_trail(): - speck = SpeckBlockCipher(number_of_rounds=5) + speck = speck_5rounds sat = SatXorDifferentialModel(speck) plaintext = set_fixed_variables(component_id='plaintext', constraint_type='not_equal', bit_positions=range(32), bit_values=integer_to_bit_list(0, 32, 'big')) @@ -103,53 +104,46 @@ def test_build_xor_differential_trail_model_fixed_weight_and_parkissat(): assert int(result['total_weight']) == int(3.0) -def test_differential_in_related_key_scenario_speck3264(): - def repeat_input_difference(input_difference_, number_of_samples_, number_of_bytes_): - bytes_array = input_difference_.to_bytes(number_of_bytes_, 'big') - np_array = np.array(list(bytes_array), dtype=np.uint8) - column_array = np_array.reshape(-1, 1) - return np.tile(column_array, (1, number_of_samples_)) +def repeat_input_difference(input_difference_, number_of_samples_, number_of_bytes_): + bytes_array = input_difference_.to_bytes(number_of_bytes_, 'big') + np_array = np.array(list(bytes_array), dtype=np.uint8) + column_array = np_array.reshape(-1, 1) + return np.tile(column_array, (1, number_of_samples_)) +def test_differential_in_related_key_scenario_speck3264(): rng = np.random.default_rng(seed=42) - number_of_samples = 2**22 - input_difference = 0x2a14001 - output_difference = 0x850a810a - key_difference = 0x2800020000800001 + number_of_samples = 2**23 + input_difference = 0x00402000 + output_difference = 0x0 + key_difference = 0x000afa3d0030a000 input_difference_data = repeat_input_difference(input_difference, number_of_samples, 4) output_difference_data = repeat_input_difference(output_difference, number_of_samples, 4) key_difference_data = repeat_input_difference(key_difference, number_of_samples, 8) - speck = SpeckBlockCipher(block_bit_size=32, key_bit_size=64, number_of_rounds=7) key_data = rng.integers(low=0, high=256, size=(8, number_of_samples), dtype=np.uint8) - plaintext_data1 = rng.integers(low=0, high=256, size=(4, number_of_samples), dtype=np.uint8) plaintext_data2 = plaintext_data1 ^ input_difference_data - ciphertext1 = speck.evaluate_vectorized([plaintext_data1, key_data]) - ciphertext2 = speck.evaluate_vectorized([plaintext_data2, key_data ^ key_difference_data]) - total = np.sum(ciphertext1[0] ^ ciphertext2[0] == output_difference_data.T) + ciphertext1 = speck_5rounds.evaluate_vectorized([plaintext_data1, key_data]) + ciphertext2 = speck_5rounds.evaluate_vectorized([plaintext_data2, key_data ^ key_difference_data]) + rows_all_true = np.all((ciphertext1[0] ^ ciphertext2[0] == output_difference_data.T), axis=1) + total = np.count_nonzero(rows_all_true) import math - total_prob_weight = math.log(total, 2) - assert 18 > total_prob_weight > 12 + total_prob_weight = math.log(total/number_of_samples, 2) + assert 21 > abs(total_prob_weight) > 12 def test_differential_in_single_key_scenario_speck3264(): - def repeat_input_difference(input_difference_, number_of_samples_, number_of_bytes_): - bytes_array = input_difference_.to_bytes(number_of_bytes_, 'big') - np_array = np.array(list(bytes_array), dtype=np.uint8) - column_array = np_array.reshape(-1, 1) - return np.tile(column_array, (1, number_of_samples_)) - rng = np.random.default_rng(seed=42) - number_of_samples = 2**22 + number_of_samples = 2**23 input_difference = 0x20400040 output_difference = 0x106040E0 input_difference_data = repeat_input_difference(input_difference, number_of_samples, 4) output_difference_data = repeat_input_difference(output_difference, number_of_samples, 4) - speck = SpeckBlockCipher(block_bit_size=32, key_bit_size=64, number_of_rounds=5) key_data = rng.integers(low=0, high=256, size=(8, number_of_samples), dtype=np.uint8) plaintext_data1 = rng.integers(low=0, high=256, size=(4, number_of_samples), dtype=np.uint8) plaintext_data2 = plaintext_data1 ^ input_difference_data - ciphertext1 = speck.evaluate_vectorized([plaintext_data1, key_data]) - ciphertext2 = speck.evaluate_vectorized([plaintext_data2, key_data]) - total = np.sum(ciphertext1[0] ^ ciphertext2[0] == output_difference_data.T) + ciphertext1 = speck_5rounds.evaluate_vectorized([plaintext_data1, key_data]) + ciphertext2 = speck_5rounds.evaluate_vectorized([plaintext_data2, key_data]) + rows_all_true = np.all((ciphertext1[0] ^ ciphertext2[0] == output_difference_data.T), axis=1) + total = np.count_nonzero(rows_all_true) import math - total_prob_weight = math.log(total, 2) - assert 21 > total_prob_weight > 13 \ No newline at end of file + total_prob_weight = math.log(total/number_of_samples, 2) + assert 21 > abs(total_prob_weight) > 13 \ No newline at end of file diff --git a/tests/unit/cipher_modules/report_test.py b/tests/unit/cipher_modules/report_test.py index 325ab5c8..e7842ba0 100644 --- a/tests/unit/cipher_modules/report_test.py +++ b/tests/unit/cipher_modules/report_test.py @@ -71,7 +71,7 @@ def test_save_as_latex_table(): def test_save_as_DataFrame(): speck = SpeckBlockCipher(number_of_rounds=2) - smt = CpXorDifferentialModel(speck) + cp = CpXorDifferentialModel(speck) plaintext = set_fixed_variables( component_id='plaintext', constraint_type='not_equal', @@ -82,7 +82,7 @@ def test_save_as_DataFrame(): constraint_type='equal', bit_positions=range(64), bit_values=(0,) * 64) - trail = smt.find_lowest_weight_xor_differential_trail(fixed_values=[plaintext, key]) + trail = cp.find_lowest_weight_xor_differential_trail(fixed_values=[plaintext, key]) algebraic_results = AlgebraicTests(speck).algebraic_tests(timeout=1) algebraic_report = Report(speck, algebraic_results) @@ -155,4 +155,20 @@ def test_show(): avalanche_results = AvalancheTests(speck).avalanche_tests() avalanche_report = Report(speck,avalanche_results) - avalanche_report.show() \ No newline at end of file + avalanche_report.show() + + milp = MilpXorDifferentialModel(speck) + plaintext = set_fixed_variables( + component_id='plaintext', + constraint_type='not_equal', + bit_positions=range(32), + bit_values=(0,) * 32) + key = set_fixed_variables( + component_id='key', + constraint_type='equal', + bit_positions=range(64), + bit_values=(0,) * 64) + + trail = milp.find_one_xor_differential_trail(fixed_values=[plaintext, key]) + trail_report = Report(speck, trail) + trail_report.show() \ No newline at end of file