Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix component analysis #182

Closed
wants to merge 11 commits into from
29 changes: 16 additions & 13 deletions claasp/cipher_modules/component_analysis_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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"
Expand Down
6 changes: 3 additions & 3 deletions claasp/cipher_modules/neural_network_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand All @@ -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()

Expand Down
2 changes: 1 addition & 1 deletion claasp/cipher_modules/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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'))
Expand All @@ -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)
Expand All @@ -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'))
Expand Down Expand Up @@ -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
total_prob_weight = math.log(total/number_of_samples, 2)
assert 21 > abs(total_prob_weight) > 13
22 changes: 19 additions & 3 deletions tests/unit/cipher_modules/report_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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)
Expand Down Expand Up @@ -155,4 +155,20 @@ def test_show():

avalanche_results = AvalancheTests(speck).avalanche_tests()
avalanche_report = Report(speck,avalanche_results)
avalanche_report.show()
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()
Loading