diff --git a/.github/workflows/run-benchmark-tests.yaml b/.github/workflows/run-benchmark-tests.yaml
index 1bc357fa..624a8b57 100644
--- a/.github/workflows/run-benchmark-tests.yaml
+++ b/.github/workflows/run-benchmark-tests.yaml
@@ -1,6 +1,6 @@
-name: Run benchmark tests
+name: run-benchmark-tests
on:
- pull_request_target:
+ pull_request:
types: [ opened, synchronize, reopened, edited ]
branches:
- main
diff --git a/.github/workflows/run-pytest-and-sonarcloud-scan.yaml b/.github/workflows/run-pytest-and-sonarcloud-scan.yaml
index fb296698..f5789af4 100644
--- a/.github/workflows/run-pytest-and-sonarcloud-scan.yaml
+++ b/.github/workflows/run-pytest-and-sonarcloud-scan.yaml
@@ -4,7 +4,7 @@ on:
push:
branches:
- '**'
- pull_request_target:
+ pull_request:
types: [opened, synchronize, reopened, edited]
branches:
- develop
@@ -47,11 +47,10 @@ jobs:
path: /home/runner/_work/claasp/coverage.xml
run-code-coverage:
- if: ${{ !github.event.repository.fork }}
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v2
with:
persist-credentials: false
fetch-depth: 0
@@ -73,4 +72,4 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- needs: run-pytest
+ needs: run-pytest
\ No newline at end of file
diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml
index cf3c7bd6..4a4fbcf3 100644
--- a/.github/workflows/update-changelog.yaml
+++ b/.github/workflows/update-changelog.yaml
@@ -31,7 +31,7 @@ jobs:
uses: actions-js/push@master
if: ${{ env.should_add_last_changes_to_master == 'true' }}
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
+ github_token: ${{ secrets.AUTHORIZATION_TOKEN }}
message: 'Changelog version updated'
tags: true
force: true
@@ -49,7 +49,7 @@ jobs:
if: ${{ env.should_add_last_changes_to_master == 'true' }}
uses: rickstaa/action-create-tag@v1
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
+ github_token: ${{ secrets.AUTHORIZATION_TOKEN }}
tag: ${{ env.tag_name }}
tag_exists_error: false
message: ${{ env.release_message }}
@@ -80,5 +80,5 @@ jobs:
with:
target: 'develop'
source: 'main'
- token: ${{ secrets.GITHUB_TOKEN }}
+ token: ${{ secrets.AUTHORIZATION_TOKEN }}
strategy_options: 'ours'
diff --git a/VERSION b/VERSION
index 852700e1..a6316f06 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v2.1.0
\ No newline at end of file
+v2.3.0
\ No newline at end of file
diff --git a/claasp/cipher_modules/algebraic_tests.py b/claasp/cipher_modules/algebraic_tests.py
index 5362c386..c021abc6 100644
--- a/claasp/cipher_modules/algebraic_tests.py
+++ b/claasp/cipher_modules/algebraic_tests.py
@@ -31,26 +31,26 @@ class AlgebraicTests:
sage: toyspn = ToySPN1(number_of_rounds=2)
sage: alg_test = AlgebraicTests(toyspn)
sage: alg_test.algebraic_tests(timeout_in_seconds=10)
- {'input_parameters': {'cipher.id': 'toyspn1_p6_k6_o6_r2',
+ {'input_parameters': {'cipher': toyspn1_p6_k6_o6_r2,
'timeout_in_seconds': 10,
'test_name': 'algebraic_tests'},
- 'test_results': {'number_of_variables': [66, 126],
- 'number_of_equations': [76, 158],
- 'number_of_monomials': [96, 186],
+ 'test_results': {'number_of_variables': [30, 48],
+ 'number_of_equations': [40, 80],
+ 'number_of_monomials': [60, 108],
'max_degree_of_equations': [2, 2],
- 'test_passed': [False, True]}}
+ 'test_passed': [False, False]}}
sage: from claasp.cipher_modules.algebraic_tests import AlgebraicTests
sage: from claasp.ciphers.block_ciphers.speck_block_cipher import SpeckBlockCipher
sage: speck = SpeckBlockCipher(number_of_rounds=1)
sage: alg_test = AlgebraicTests(speck)
sage: alg_test.algebraic_tests(timeout_in_seconds=30)
- {'input_parameters': {'cipher.id': 'speck_p32_k64_o32_r1',
+ {'input_parameters': {'cipher': speck_p32_k64_o32_r1,
'timeout_in_seconds': 30,
'test_name': 'algebraic_tests'},
- 'test_results': {'number_of_variables': [320],
- 'number_of_equations': [272],
- 'number_of_monomials': [365],
+ 'test_results': {'number_of_variables': [144],
+ 'number_of_equations': [96],
+ 'number_of_monomials': [189],
'max_degree_of_equations': [2],
'test_passed': [True]}}
@@ -58,22 +58,23 @@ class AlgebraicTests:
def __init__(self, cipher):
self._cipher = cipher
+ self._algebraic_model = AlgebraicModel(cipher)
def algebraic_tests(self, timeout_in_seconds=60):
from sage.structure.sequence import Sequence
nvars_up_to_round = []
-
npolynomials_up_to_round = []
nmonomials_up_to_round = []
max_deg_of_equations_up_to_round = []
tests_up_to_round = []
F = []
-
- algebraic_model = AlgebraicModel(self._cipher)
+ constant_vars = {}
for round_number in range(self._cipher.number_of_rounds):
- F += algebraic_model.polynomial_system_at_round(round_number) + \
- algebraic_model.connection_polynomials_at_round(round_number)
+ F += self._algebraic_model.polynomial_system_at_round(round_number, True)
+ constant_vars.update(self._algebraic_model._dict_constant_component_polynomials(round_number))
+ if constant_vars is not None:
+ F = self._algebraic_model._remove_constant_polynomials(constant_vars, F)
Fseq = Sequence(F)
nvars_up_to_round.append(Fseq.nvariables())
npolynomials_up_to_round.append(len(Fseq))
diff --git a/claasp/cipher_modules/models/algebraic/algebraic_model.py b/claasp/cipher_modules/models/algebraic/algebraic_model.py
index efdf4826..a9543d2e 100644
--- a/claasp/cipher_modules/models/algebraic/algebraic_model.py
+++ b/claasp/cipher_modules/models/algebraic/algebraic_model.py
@@ -1,4 +1,3 @@
-
# ****************************************************************************
# Copyright 2023 Technology Innovation Institute
#
@@ -78,25 +77,11 @@ def connection_polynomials_at_round(self, r):
plaintext_y23 + sbox_0_5_x3]
"""
polynomials = []
- R = self.ring()
for component in self._cipher.get_components_in_round(r):
-
if component.type == "constant":
continue
-
- input_vars = [component.id + "_" + self.input_postfix + str(i) for i in range(component.input_bit_size)]
- input_vars = list(map(R, input_vars))
-
- input_links = component.input_id_links
- input_positions = component.input_bit_positions
-
- prev_input_vars = []
- for k in range(len(input_links)):
- prev_input_vars += [input_links[k] + "_" + self.output_postfix + str(i) for i in
- input_positions[k]]
- prev_input_vars = list(map(R, prev_input_vars))
-
+ input_vars, prev_input_vars = self._input_vars_previous_input_vars(component)
polynomials += [x + y for (x, y) in zip(input_vars, prev_input_vars)]
return polynomials
@@ -107,15 +92,15 @@ def is_algebraically_secure(self, timeout):
INPUT:
- - ``timeout`` -- **integer**; the timeout for the Grobner basis computation in seconds
+ - ``timeout`` -- **integer**; the timeout for the Groebner basis computation in seconds
EXAMPLES::
sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
- sage: from claasp.ciphers.block_ciphers.identity_block_cipher import IdentityBlockCipher
- sage: identity = IdentityBlockCipher()
- sage: algebraic = AlgebraicModel(identity)
- sage: algebraic.is_algebraically_secure(120)
+ sage: from claasp.ciphers.toys.toyspn1 import ToySPN1
+ sage: toyspn = ToySPN1()
+ sage: algebraic = AlgebraicModel(toyspn)
+ sage: algebraic.is_algebraically_secure(30)
False
"""
from cysignals.alarm import alarm, cancel_alarm
@@ -164,18 +149,48 @@ def polynomial_system(self):
EXAMPLES::
+ sage: from claasp.ciphers.toys.toyspn1 import ToySPN1
+ sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
+ sage: toyspn = ToySPN1()
+ sage: AlgebraicModel(toyspn).polynomial_system()
+ Polynomial Sequence with 80 Polynomials in 48 Variables
+
sage: from claasp.ciphers.block_ciphers.fancy_block_cipher import FancyBlockCipher
sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
sage: fancy = FancyBlockCipher(number_of_rounds=1)
- sage: AlgebraicModel(fancy).polynomial_system() # long time
- Polynomial Sequence with 468 Polynomials in 384 Variables
+ sage: AlgebraicModel(fancy).polynomial_system()
+ Polynomial Sequence with 228 Polynomials in 144 Variables
+
+ sage: from claasp.ciphers.block_ciphers.speck_block_cipher import SpeckBlockCipher
+ sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
+ sage: speck = SpeckBlockCipher(number_of_rounds=2)
+ sage: AlgebraicModel(speck).polynomial_system()
+ Polynomial Sequence with 288 Polynomials in 352 Variables
+
+ sage: from claasp.ciphers.block_ciphers.aes_block_cipher import AESBlockCipher
+ sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
+ sage: aes = AESBlockCipher(word_size=4, state_size=2, number_of_rounds=1)
+ sage: AlgebraicModel(aes).polynomial_system()
+ Polynomial Sequence with 198 Polynomials in 128 Variables
+
+ sage: from claasp.ciphers.block_ciphers.tea_block_cipher import TeaBlockCipher
+ sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
+ sage: tea = TeaBlockCipher(block_bit_size=32, key_bit_size=64, number_of_rounds=1)
+ sage: AlgebraicModel(tea).polynomial_system()
+ Polynomial Sequence with 352 Polynomials in 448 Variables
+
"""
- polynomials = sum([self.polynomial_system_at_round(r) for r in range(self._cipher.number_of_rounds)], [])
- polynomials += self.connection_polynomials()
+ polynomials = []
+ constant_vars = {}
+ for r in range(self._cipher.number_of_rounds):
+ polynomials += self.polynomial_system_at_round(r, True)
+ constant_vars.update(self._dict_constant_component_polynomials(r))
+ if constant_vars is not None:
+ polynomials = self._remove_constant_polynomials(constant_vars, polynomials)
return Sequence(polynomials)
- def polynomial_system_at_round(self, r):
+ def polynomial_system_at_round(self, r, fun_call_flag=False):
"""
Return a polynomial system at round `r`.
@@ -188,8 +203,8 @@ def polynomial_system_at_round(self, r):
sage: from claasp.ciphers.block_ciphers.fancy_block_cipher import FancyBlockCipher
sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
sage: fancy = FancyBlockCipher(number_of_rounds=1)
- sage: AlgebraicModel(fancy).polynomial_system_at_round(0) # long time
- Polynomial Sequence with 252 Polynomials in 288 Variables
+ sage: AlgebraicModel(fancy).polynomial_system_at_round(0)
+ Polynomial Sequence with 228 Polynomials in 144 Variables
"""
if not 0 <= r < self._cipher.number_of_rounds:
raise ValueError(f"r must be in the range 0 <= r < {self._cipher.number_of_rounds}")
@@ -209,8 +224,70 @@ def polynomial_system_at_round(self, r):
operation in ['ROTATE_BY_VARIABLE_AMOUNT', 'SHIFT_BY_VARIABLE_AMOUNT']:
raise ValueError(f"polynomial generation of {operation} operation is not supported at present")
+ polynomials = self._apply_connection_variable_mapping(Sequence(polynomials), r)
+
+ if fun_call_flag is False:
+ constant_vars = self._dict_constant_component_polynomials(r)
+ if constant_vars is not None:
+ polynomials = self._remove_constant_polynomials(constant_vars, polynomials)
+
return Sequence(polynomials)
+ def _apply_connection_variable_mapping(self, polys, r):
+
+ if not polys:
+ return polys
+
+ variable_substitution_dict = {}
+
+ for component in self._cipher.get_components_in_round(r):
+ if component.type == "constant":
+ continue
+ input_vars, prev_input_vars = self._input_vars_previous_input_vars(component)
+ if component.type != "cipher_output":
+ variable_substitution_dict.update({x: y for x, y in zip(input_vars, prev_input_vars)})
+ else:
+ variable_substitution_dict.update({y: x for x, y in zip(input_vars, prev_input_vars)})
+
+ polys = polys.subs(variable_substitution_dict)
+
+ return polys
+
+ def _input_vars_previous_input_vars(self, component):
+ input_vars = [component.id + "_" + self.input_postfix + str(i) for i in range(component.input_bit_size)]
+ input_vars = list(map(self.ring(), input_vars))
+ input_links = component.input_id_links
+ input_positions = component.input_bit_positions
+
+ prev_input_vars = []
+ for k in range(len(input_links)):
+ prev_input_vars += [input_links[k] + "_" + self.output_postfix + str(i) for i in
+ input_positions[k]]
+ prev_input_vars = list(map(self.ring(), prev_input_vars))
+ return input_vars, prev_input_vars
+
+ def _dict_constant_component_polynomials(self, round_number):
+
+ constant_vars = {}
+ for component in self._cipher.get_components_in_round(round_number):
+ if component.type == "constant":
+ output_vars = [component.id + "_" + self.output_postfix + str(i) for i in
+ range(component.output_bit_size)]
+ else:
+ continue
+ output_vars = list(map(self.ring(), output_vars))
+ constant = int(component.description[0], 16)
+ b = list(map(int, reversed(bin(constant)[2:])))
+ b += [0] * (component.output_bit_size - len(b))
+ constant_vars.update({x: y for x, y in zip(output_vars, b)})
+ return constant_vars
+
+ def _remove_constant_polynomials(self, constant_vars, polys):
+
+ polys = Sequence(polys).subs(constant_vars)
+ polys = [p for p in polys if p != 0]
+ return polys
+
def ring(self):
"""
Return the polynomial ring for the system of equations.
diff --git a/claasp/cipher_modules/models/milp/milp_model.py b/claasp/cipher_modules/models/milp/milp_model.py
index f43e35c6..6b40439e 100644
--- a/claasp/cipher_modules/models/milp/milp_model.py
+++ b/claasp/cipher_modules/models/milp/milp_model.py
@@ -43,14 +43,11 @@
from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT, get_external_milp_solver_configuration
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT, MODEL_DEFAULT_PATH, MILP_SOLVERS_EXTERNAL, \
+ MILP_SOLVERS_INTERNAL
from claasp.cipher_modules.models.milp.utils.utils import _get_data, _parse_external_solver_output, _write_model_to_lp_file
from claasp.cipher_modules.models.utils import convert_solver_solution_to_dictionary
-verbose = 0
-verbose_print = print if verbose else lambda *a, **k: None
-
-
def get_independent_input_output_variables(component):
"""
Return a list of 2 lists containing the name of each input/output bit.
@@ -132,7 +129,7 @@ def get_input_output_variables(component):
class MilpModel:
"""Build MILP models for ciphers using Cipher."""
- def __init__(self, cipher, n_window_heuristic=None):
+ def __init__(self, cipher, n_window_heuristic=None, verbose=False):
self._cipher = cipher
self._variables_list = []
self._model_constraints = []
@@ -143,6 +140,7 @@ def __init__(self, cipher, n_window_heuristic=None):
self._non_linear_component_id = []
self._intermediate_output_names = []
self._number_of_trails_found = 0
+ self._verbose_print = print if verbose else lambda *a, **k: None
def fix_variables_value_constraints(self, fixed_variables=[]):
"""
@@ -259,6 +257,8 @@ def init_model_in_sage_milp_class(self, solver_name=SOLVER_DEFAULT):
sage: milp._model
Mixed Integer Program (no objective, 0 variables, 0 constraints)
"""
+ if solver_name.upper().endswith("_EXT"):
+ solver_name = SOLVER_DEFAULT
self._model = MixedIntegerLinearProgram(maximization=False, solver=solver_name)
self._binary_variable = self._model.new_variable(binary=True)
self._integer_variable = self._model.new_variable(integer=True)
@@ -266,17 +266,21 @@ def init_model_in_sage_milp_class(self, solver_name=SOLVER_DEFAULT):
def _solve_with_external_solver(self, model_type, model_path, solver_name=SOLVER_DEFAULT):
- solvers_configuration = get_external_milp_solver_configuration(f'{model_path[:-3]}.sol')
- if solver_name not in solvers_configuration:
- raise ValueError(f"Invalid solver name: {solver_name}."
- f"Currently supported solvers are 'Gurobi', 'cplex', 'scip' and 'glpk'.")
- solver_specs = solvers_configuration[solver_name]
- command = solver_specs['command']
- options = solver_specs['options']
- tracemalloc.start()
+ solver_specs = [specs for specs in MILP_SOLVERS_EXTERNAL if specs["solver_name"] == solver_name.upper()][0]
+ solution_file_path = f'{MODEL_DEFAULT_PATH}/{model_path[:-3]}.sol'
- command += model_path + options
+ command = ""
+ for key in solver_specs['keywords']['command']['format']:
+ parameter = solver_specs['keywords']['command'][key]
+ if key == "input_file":
+ parameter += " " + model_path
+ elif key == "output_file":
+ parameter = parameter + solution_file_path if parameter.endswith('=') else parameter + " " + solution_file_path
+ elif key == "options":
+ parameter = " ".join(parameter)
+ command += " " + parameter
+ tracemalloc.start()
solver_process = subprocess.run(command, capture_output=True, shell=True, text=True)
milp_memory = tracemalloc.get_traced_memory()[1] / 10 ** 6
tracemalloc.stop()
@@ -285,29 +289,29 @@ def _solve_with_external_solver(self, model_type, model_path, solver_name=SOLVER
raise MIPSolverException("Make sure that the solver is correctly installed.")
if 'memory' in solver_specs:
- milp_memory = _get_data(solver_specs['memory'], str(solver_process))
+ milp_memory = _get_data(solver_specs['keywords']['memory'], str(solver_process))
- return _parse_external_solver_output(self, solvers_configuration, model_type, solver_name, solver_process) + (milp_memory,)
+ return _parse_external_solver_output(self, solver_specs, model_type, solution_file_path, solver_process.stdout) + (milp_memory,)
def _solve_with_internal_solver(self):
mip = self._model
-
- verbose_print("Solving model in progress ...")
+ status = 'UNSATISFIABLE'
+ self._verbose_print("Solving model in progress ...")
time_start = time.time()
tracemalloc.start()
try:
mip.solve()
- milp_memory = tracemalloc.get_traced_memory()[1] / 10 ** 6
- tracemalloc.stop()
- time_end = time.time()
- milp_time = time_end - time_start
- verbose_print(f"Time for solving the model = {milp_time}")
status = 'SATISFIABLE'
except MIPSolverException as milp_exception:
- status = 'UNSATISFIABLE'
print(milp_exception)
+ finally:
+ milp_memory = tracemalloc.get_traced_memory()[1] / 10 ** 6
+ tracemalloc.stop()
+ time_end = time.time()
+ milp_time = time_end - time_start
+ self._verbose_print(f"Time for solving the model = {milp_time}")
return status, milp_time, milp_memory
@@ -332,23 +336,44 @@ def solve(self, model_type, solver_name=SOLVER_DEFAULT, external_solver_name=Non
...
sage: solution = milp.solve("xor_differential") # random
"""
- if external_solver_name:
- solver_name_in_solution = external_solver_name
+ if external_solver_name or (solver_name.upper().endswith("_EXT")):
+ solver_choice = external_solver_name or solver_name
+ if solver_choice.upper() not in [specs["solver_name"] for specs in MILP_SOLVERS_EXTERNAL]:
+ raise ValueError(f"Invalid solver name: {solver_choice}.\n"
+ f"Please select a solver in the following list: {[specs['solver_name'] for specs in MILP_SOLVERS_EXTERNAL]}.")
+
+ solver_name_in_solution = solver_choice
model_path = _write_model_to_lp_file(self, model_type)
solution_file_path, status, objective_value, components_values, milp_time, milp_memory = self._solve_with_external_solver(
- model_type, model_path, external_solver_name)
+ model_type, model_path, solver_choice)
os.remove(model_path)
os.remove(f"{solution_file_path}")
else:
+ objective_value = None
+ components_values = None
solver_name_in_solution = solver_name
status, milp_time, milp_memory = self._solve_with_internal_solver()
- objective_value, components_values = self._parse_solver_output()
+ if status == 'SATISFIABLE':
+ objective_value, components_values = self._parse_solver_output()
solution = convert_solver_solution_to_dictionary(self._cipher, model_type, solver_name_in_solution, milp_time,
milp_memory, components_values, objective_value)
solution['status'] = status
return solution
+ def solver_names(self, verbose=False):
+ solver_names = []
+
+ keys = ['solver_brand_name', 'solver_name']
+ for solver in MILP_SOLVERS_INTERNAL:
+ solver_names.append({key: solver[key] for key in keys})
+ if verbose:
+ keys = ['solver_brand_name', 'solver_name', 'keywords']
+
+ for solver in MILP_SOLVERS_EXTERNAL:
+ solver_names.append({key: solver[key] for key in keys})
+ return solver_names
+
@property
def binary_variable(self):
return self._binary_variable
diff --git a/claasp/cipher_modules/models/milp/milp_models/milp_bitwise_deterministic_truncated_xor_differential_model.py b/claasp/cipher_modules/models/milp/milp_models/milp_bitwise_deterministic_truncated_xor_differential_model.py
index 7dbc3de4..5eccd65e 100644
--- a/claasp/cipher_modules/models/milp/milp_models/milp_bitwise_deterministic_truncated_xor_differential_model.py
+++ b/claasp/cipher_modules/models/milp/milp_models/milp_bitwise_deterministic_truncated_xor_differential_model.py
@@ -16,12 +16,12 @@
# ****************************************************************************
import time
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT
from claasp.cipher_modules.models.milp.utils.milp_name_mappings import MILP_BITWISE_DETERMINISTIC_TRUNCATED, \
MILP_BACKWARD_SUFFIX, MILP_BUILDING_MESSAGE, MILP_TRUNCATED_XOR_DIFFERENTIAL_OBJECTIVE
from claasp.cipher_modules.models.milp.utils.milp_truncated_utils import \
fix_variables_value_deterministic_truncated_xor_differential_constraints
-from claasp.cipher_modules.models.milp.milp_model import MilpModel, verbose_print
+from claasp.cipher_modules.models.milp.milp_model import MilpModel
from claasp.cipher_modules.models.utils import set_component_solution
from claasp.name_mappings import (CONSTANT, INTERMEDIATE_OUTPUT, CIPHER_OUTPUT,
WORD_OPERATION, LINEAR_LAYER, SBOX, MIX_COLUMN)
@@ -29,8 +29,8 @@
class MilpBitwiseDeterministicTruncatedXorDifferentialModel(MilpModel):
- def __init__(self, cipher, n_window_heuristic=None):
- super().__init__(cipher, n_window_heuristic)
+ def __init__(self, cipher, n_window_heuristic=None, verbose=False):
+ super().__init__(cipher, n_window_heuristic, verbose)
self._trunc_binvar = None
def init_model_in_sage_milp_class(self, solver_name=SOLVER_DEFAULT):
@@ -80,7 +80,7 @@ def add_constraints_to_build_in_sage_milp_class(self, fixed_variables=[]):
sage: milp.add_constraints_to_build_in_sage_milp_class()
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
mip = self._model
x = self._binary_variable
@@ -305,7 +305,7 @@ def find_one_bitwise_deterministic_truncated_xor_differential_trail(self, fixed_
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(fixed_values)
@@ -344,7 +344,7 @@ def find_lowest_varied_patterns_bitwise_deterministic_truncated_xor_differential
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
p = self._integer_variable
mip.set_objective(p[MILP_TRUNCATED_XOR_DIFFERENTIAL_OBJECTIVE])
diff --git a/claasp/cipher_modules/models/milp/milp_models/milp_bitwise_impossible_xor_differential_model.py b/claasp/cipher_modules/models/milp/milp_models/milp_bitwise_impossible_xor_differential_model.py
index 93d85e4d..e9931a47 100644
--- a/claasp/cipher_modules/models/milp/milp_models/milp_bitwise_impossible_xor_differential_model.py
+++ b/claasp/cipher_modules/models/milp/milp_models/milp_bitwise_impossible_xor_differential_model.py
@@ -18,8 +18,7 @@
import time
from claasp.cipher_modules.inverse_cipher import get_key_schedule_component_ids
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT
-from claasp.cipher_modules.models.milp.milp_model import verbose_print
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT
from claasp.cipher_modules.models.milp.milp_models.milp_bitwise_deterministic_truncated_xor_differential_model import \
MilpBitwiseDeterministicTruncatedXorDifferentialModel
from claasp.cipher_modules.models.milp.utils.milp_name_mappings import MILP_BITWISE_IMPOSSIBLE, \
@@ -30,8 +29,8 @@
class MilpBitwiseImpossibleXorDifferentialModel(MilpBitwiseDeterministicTruncatedXorDifferentialModel):
- def __init__(self, cipher, n_window_heuristic=None):
- super().__init__(cipher, n_window_heuristic)
+ def __init__(self, cipher, n_window_heuristic=None, verbose=False):
+ super().__init__(cipher, n_window_heuristic, verbose)
self._forward_cipher = None
self._backward_cipher = None
self._incompatible_components = None
@@ -90,7 +89,7 @@ def add_constraints_to_build_in_sage_milp_class(self, middle_round=None, fixed_v
sage: milp.add_constraints_to_build_in_sage_milp_class(1)
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
mip = self._model
x = self._binary_variable
x_class = self._trunc_binvar
@@ -161,7 +160,7 @@ def add_constraints_to_build_in_sage_milp_class_with_chosen_incompatible_compone
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
mip = self._model
x = self._binary_variable
x_class = self._trunc_binvar
@@ -254,7 +253,7 @@ def add_constraints_to_build_fully_automatic_model_in_sage_milp_class(self, fixe
sage: milp.add_constraints_to_build_fully_automatic_model_in_sage_milp_class()
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
mip = self._model
x = self._binary_variable
@@ -335,7 +334,7 @@ def find_one_bitwise_impossible_xor_differential_trail(self, middle_round, fixed
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(middle_round, fixed_values)
@@ -389,7 +388,7 @@ def find_one_bitwise_impossible_xor_differential_trail_with_chosen_incompatible_
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class_with_chosen_incompatible_components(component_id_list, fixed_values)
@@ -442,7 +441,7 @@ def find_one_bitwise_impossible_xor_differential_trail_with_fully_automatic_mode
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_fully_automatic_model_in_sage_milp_class(fixed_variables=fixed_values, include_all_components=include_all_components)
diff --git a/claasp/cipher_modules/models/milp/milp_models/milp_wordwise_deterministic_truncated_xor_differential_model.py b/claasp/cipher_modules/models/milp/milp_models/milp_wordwise_deterministic_truncated_xor_differential_model.py
index 4856ba6e..34348713 100644
--- a/claasp/cipher_modules/models/milp/milp_models/milp_wordwise_deterministic_truncated_xor_differential_model.py
+++ b/claasp/cipher_modules/models/milp/milp_models/milp_wordwise_deterministic_truncated_xor_differential_model.py
@@ -16,8 +16,8 @@
# ****************************************************************************
import time
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT
-from claasp.cipher_modules.models.milp.milp_model import MilpModel, verbose_print
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT
+from claasp.cipher_modules.models.milp.milp_model import MilpModel
from claasp.cipher_modules.models.milp.utils.milp_name_mappings import MILP_WORDWISE_DETERMINISTIC_TRUNCATED, \
MILP_BUILDING_MESSAGE, MILP_TRUNCATED_XOR_DIFFERENTIAL_OBJECTIVE
from claasp.cipher_modules.models.milp.utils.utils import espresso_pos_to_constraints, \
@@ -37,8 +37,8 @@
class MilpWordwiseDeterministicTruncatedXorDifferentialModel(MilpModel):
- def __init__(self, cipher, n_window_heuristic=None):
- super().__init__(cipher, n_window_heuristic)
+ def __init__(self, cipher, n_window_heuristic=None, verbose=False):
+ super().__init__(cipher, n_window_heuristic, verbose)
self._trunc_wordvar = None
self._word_size = 4
if self._cipher.is_spn():
@@ -96,7 +96,7 @@ def add_constraints_to_build_in_sage_milp_class(self, fixed_bits=[], fixed_words
sage: milp.add_constraints_to_build_in_sage_milp_class()
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
mip = self._model
x = self._binary_variable
@@ -323,7 +323,7 @@ def find_one_wordwise_deterministic_truncated_xor_differential_trail(self, fixed
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(fixed_bits, fixed_words)
@@ -363,7 +363,7 @@ def find_lowest_varied_patterns_wordwise_deterministic_truncated_xor_differentia
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
p = self._integer_variable
mip.set_objective(p[MILP_TRUNCATED_XOR_DIFFERENTIAL_OBJECTIVE])
diff --git a/claasp/cipher_modules/models/milp/milp_models/milp_wordwise_impossible_xor_differential_model.py b/claasp/cipher_modules/models/milp/milp_models/milp_wordwise_impossible_xor_differential_model.py
index 7bc4f7b8..7c3a4a37 100644
--- a/claasp/cipher_modules/models/milp/milp_models/milp_wordwise_impossible_xor_differential_model.py
+++ b/claasp/cipher_modules/models/milp/milp_models/milp_wordwise_impossible_xor_differential_model.py
@@ -18,24 +18,18 @@
import time
from claasp.cipher_modules.inverse_cipher import get_key_schedule_component_ids
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT
-from claasp.cipher_modules.models.milp.milp_model import verbose_print
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT
from claasp.cipher_modules.models.milp.milp_models.milp_wordwise_deterministic_truncated_xor_differential_model import MilpWordwiseDeterministicTruncatedXorDifferentialModel
-from claasp.cipher_modules.models.milp.utils.generate_inequalities_for_wordwise_truncated_xor_with_n_input_bits import \
- update_dictionary_that_contains_wordwise_truncated_input_inequalities, \
- output_dictionary_that_contains_wordwise_truncated_input_inequalities
from claasp.cipher_modules.models.milp.utils.milp_name_mappings import MILP_WORDWISE_IMPOSSIBLE_AUTO, \
MILP_WORDWISE_IMPOSSIBLE, MILP_BACKWARD_SUFFIX, MILP_BUILDING_MESSAGE
-from claasp.cipher_modules.models.milp.utils.utils import espresso_pos_to_constraints
-from claasp.cipher_modules.models.utils import set_component_solution
from claasp.name_mappings import CIPHER_OUTPUT, INPUT_KEY
from claasp.cipher_modules.models.milp.utils import utils as milp_utils, milp_truncated_utils
class MilpWordwiseImpossibleXorDifferentialModel(MilpWordwiseDeterministicTruncatedXorDifferentialModel):
- def __init__(self, cipher, n_window_heuristic=None):
- super().__init__(cipher, n_window_heuristic)
+ def __init__(self, cipher, n_window_heuristic=None, verbose=False):
+ super().__init__(cipher, n_window_heuristic, verbose)
self._forward_cipher = None
self._backward_cipher = None
self._incompatible_components = None
@@ -97,7 +91,7 @@ def add_constraints_to_build_in_sage_milp_class(self, middle_round=None, fixed_b
sage: milp.add_constraints_to_build_in_sage_milp_class(1, get_single_key_scenario_format_for_fixed_values(aes))
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
mip = self._model
x = self._binary_variable
@@ -177,7 +171,7 @@ def add_constraints_to_build_in_sage_milp_class_with_chosen_incompatible_compone
sage: milp.add_constraints_to_build_in_sage_milp_class_with_fixed_components(["intermediate_output_0_37"], get_single_key_scenario_format_for_fixed_values(aes))
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
mip = self._model
x = self._binary_variable
@@ -282,7 +276,7 @@ def add_constraints_to_build_fully_automatic_model_in_sage_milp_class(self, fixe
sage: milp.add_constraints_to_build_fully_automatic_model_in_sage_milp_class(get_single_key_scenario_format_for_fixed_values(aes))
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
mip = self._model
x = self._binary_variable
@@ -337,7 +331,7 @@ def find_one_wordwise_impossible_xor_differential_trail(self, middle_round=None,
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(middle_round, fixed_bits, fixed_words)
@@ -372,7 +366,7 @@ def find_one_wordwise_impossible_xor_differential_trail_with_chosen_components(s
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class_with_chosen_incompatible_components(component_id_list,
@@ -409,7 +403,7 @@ def find_one_wordwise_impossible_xor_differential_trail_with_fully_automatic_mod
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_fully_automatic_model_in_sage_milp_class(fixed_bits, fixed_words, include_all_components=include_all_components)
diff --git a/claasp/cipher_modules/models/milp/milp_models/milp_xor_differential_model.py b/claasp/cipher_modules/models/milp/milp_models/milp_xor_differential_model.py
index 2c9e186e..81be9427 100644
--- a/claasp/cipher_modules/models/milp/milp_models/milp_xor_differential_model.py
+++ b/claasp/cipher_modules/models/milp/milp_models/milp_xor_differential_model.py
@@ -20,8 +20,8 @@
from bitstring import BitArray
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT
-from claasp.cipher_modules.models.milp.milp_model import MilpModel, verbose_print
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT
+from claasp.cipher_modules.models.milp.milp_model import MilpModel
from claasp.cipher_modules.models.milp.utils.milp_name_mappings import MILP_XOR_DIFFERENTIAL, MILP_PROBABILITY_SUFFIX, \
MILP_BUILDING_MESSAGE, MILP_XOR_DIFFERENTIAL_OBJECTIVE
from claasp.cipher_modules.models.milp.utils.utils import _string_to_hex, _get_variables_values_as_string, _filter_fixed_variables
@@ -33,8 +33,8 @@
class MilpXorDifferentialModel(MilpModel):
- def __init__(self, cipher, n_window_heuristic=None):
- super().__init__(cipher, n_window_heuristic)
+ def __init__(self, cipher, n_window_heuristic=None, verbose=False):
+ super().__init__(cipher, n_window_heuristic, verbose)
def add_constraints_to_build_in_sage_milp_class(self, weight=-1, fixed_variables=[]):
"""
@@ -65,7 +65,7 @@ def add_constraints_to_build_in_sage_milp_class(self, weight=-1, fixed_variables
sage: mip.number_of_variables()
468
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
self.build_xor_differential_trail_model(weight, fixed_variables)
mip = self._model
p = self._integer_variable
@@ -171,7 +171,7 @@ def find_all_xor_differential_trails_with_fixed_weight(self, fixed_weight, fixed
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(-1, fixed_values)
@@ -202,7 +202,7 @@ def find_all_xor_differential_trails_with_fixed_weight(self, fixed_weight, fixed
solution['building_time'] = building_time
solution['test_name'] = "find_all_xor_differential_trails_with_fixed_weight"
self._number_of_trails_found += 1
- verbose_print(f"trails found : {self._number_of_trails_found}")
+ self._verbose_print(f"trails found : {self._number_of_trails_found}")
list_trails.append(solution)
fixed_variables = self._get_fixed_variables_from_solution(fixed_values, inputs_ids, solution)
@@ -374,7 +374,7 @@ def find_all_xor_differential_trails_with_weight_at_most(self, min_weight, max_w
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(-1, fixed_values)
@@ -403,7 +403,7 @@ def find_all_xor_differential_trails_with_weight_at_most(self, min_weight, max_w
solution['building_time'] = building_time
solution['test_name'] = "find_all_xor_differential_trails_with_weight_at_most"
self._number_of_trails_found += 1
- verbose_print(f"trails found : {self._number_of_trails_found}")
+ self._verbose_print(f"trails found : {self._number_of_trails_found}")
list_trails.append(solution)
fixed_variables = self._get_fixed_variables_from_solution(fixed_values, inputs_ids, solution)
@@ -463,7 +463,7 @@ def find_lowest_weight_xor_differential_trail(self, fixed_values=[], solver_name
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
p = self._integer_variable
mip.set_objective(p[MILP_XOR_DIFFERENTIAL_OBJECTIVE])
@@ -496,7 +496,7 @@ def find_one_xor_differential_trail(self, fixed_values=[], solver_name=SOLVER_DE
EXAMPLES::
# single-key setting
- from claasp.ciphers.block_ciphers.speck_block_cipher import SpeckBlockCipher
+ sage: from claasp.ciphers.block_ciphers.speck_block_cipher import SpeckBlockCipher
sage: from claasp.cipher_modules.models.milp.milp_models.milp_xor_differential_model import MilpXorDifferentialModel
sage: speck = SpeckBlockCipher(number_of_rounds=5)
sage: milp = MilpXorDifferentialModel(speck)
@@ -513,7 +513,7 @@ def find_one_xor_differential_trail(self, fixed_values=[], solver_name=SOLVER_DE
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(-1, fixed_values)
@@ -567,7 +567,7 @@ def find_one_xor_differential_trail_with_fixed_weight(self, fixed_weight, fixed_
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(-1, fixed_values)
diff --git a/claasp/cipher_modules/models/milp/milp_models/milp_xor_linear_model.py b/claasp/cipher_modules/models/milp/milp_models/milp_xor_linear_model.py
index f2b96186..fea4b3a9 100644
--- a/claasp/cipher_modules/models/milp/milp_models/milp_xor_linear_model.py
+++ b/claasp/cipher_modules/models/milp/milp_models/milp_xor_linear_model.py
@@ -22,11 +22,11 @@
from bitstring import BitArray
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT
from claasp.cipher_modules.models.milp.utils.generate_inequalities_for_xor_with_n_input_bits import \
update_dictionary_that_contains_xor_inequalities_between_n_input_bits, \
output_dictionary_that_contains_xor_inequalities
-from claasp.cipher_modules.models.milp.milp_model import MilpModel, verbose_print
+from claasp.cipher_modules.models.milp.milp_model import MilpModel
from claasp.cipher_modules.models.milp.utils.milp_name_mappings import MILP_XOR_LINEAR, MILP_PROBABILITY_SUFFIX, \
MILP_BUILDING_MESSAGE, MILP_XOR_LINEAR_OBJECTIVE
from claasp.cipher_modules.models.milp.utils.utils import _get_variables_values_as_string, _string_to_hex, \
@@ -38,8 +38,8 @@
class MilpXorLinearModel(MilpModel):
- def __init__(self, cipher, n_window_heuristic=None):
- super().__init__(cipher, n_window_heuristic)
+ def __init__(self, cipher, n_window_heuristic=None, verbose=False):
+ super().__init__(cipher, n_window_heuristic, verbose)
self.bit_bindings, self.bit_bindings_for_intermediate_output = get_bit_bindings(cipher, '_'.join)
def add_constraints_to_build_in_sage_milp_class(self, weight=-1, fixed_variables=[]):
@@ -71,7 +71,7 @@ def add_constraints_to_build_in_sage_milp_class(self, weight=-1, fixed_variables
sage: mip.number_of_variables()
1018
"""
- verbose_print(MILP_BUILDING_MESSAGE)
+ self._verbose_print(MILP_BUILDING_MESSAGE)
self.build_xor_linear_trail_model(weight, fixed_variables)
mip = self._model
p = self._integer_variable
@@ -299,7 +299,7 @@ def find_all_xor_linear_trails_with_fixed_weight(self, fixed_weight, fixed_value
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
@@ -323,7 +323,7 @@ def find_all_xor_linear_trails_with_fixed_weight(self, fixed_weight, fixed_value
solution['building_time'] = building_time
solution['test_name'] = "find_all_xor_linear_trails_with_fixed_weight"
self._number_of_trails_found += 1
- verbose_print(f"trails found : {self._number_of_trails_found}")
+ self._verbose_print(f"trails found : {self._number_of_trails_found}")
list_trails.append(solution)
fixed_variables = self._get_fixed_variables_from_solution(fixed_values, inputs_ids, solution)
@@ -394,7 +394,7 @@ def find_all_xor_linear_trails_with_weight_at_most(self, min_weight, max_weight,
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(-1, fixed_values)
@@ -418,7 +418,7 @@ def find_all_xor_linear_trails_with_weight_at_most(self, min_weight, max_weight,
solution['building_time'] = building_time
solution['test_name'] = "find_all_xor_linear_trails_with_weight_at_most"
self._number_of_trails_found += 1
- verbose_print(f"trails found : {self._number_of_trails_found}")
+ self._verbose_print(f"trails found : {self._number_of_trails_found}")
list_trails.append(solution)
fixed_variables = self._get_fixed_variables_from_solution(fixed_values, inputs_ids, solution)
@@ -494,7 +494,7 @@ def find_lowest_weight_xor_linear_trail(self, fixed_values=[], solver_name=SOLVE
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
p = self._integer_variable
mip.set_objective(p[MILP_XOR_LINEAR_OBJECTIVE])
@@ -542,7 +542,7 @@ def find_one_xor_linear_trail(self, fixed_values=[], solver_name=SOLVER_DEFAULT,
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(-1, fixed_values)
@@ -595,7 +595,7 @@ def find_one_xor_linear_trail_with_fixed_weight(self, fixed_weight, fixed_values
"""
start = time.time()
self.init_model_in_sage_milp_class(solver_name)
- verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
+ self._verbose_print(f"Solver used : {solver_name} (Choose Gurobi for Better performance)")
mip = self._model
mip.set_objective(None)
self.add_constraints_to_build_in_sage_milp_class(-1, fixed_values)
diff --git a/claasp/cipher_modules/models/milp/solvers.py b/claasp/cipher_modules/models/milp/solvers.py
new file mode 100644
index 00000000..6f7650d3
--- /dev/null
+++ b/claasp/cipher_modules/models/milp/solvers.py
@@ -0,0 +1,120 @@
+import os
+
+# ****************************************************************************
+# Copyright 2023 Technology Innovation Institute
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# ****************************************************************************
+
+
+SOLVER_DEFAULT = "GLPK"
+MODEL_DEFAULT_PATH = os.getcwd()
+
+
+
+MILP_SOLVERS_INTERNAL = [
+ {"solver_brand_name": "GLPK (GNU Linear Programming Kit) (using Sage backend)", "solver_name": "GLPK"},
+ {"solver_brand_name": "GLPK (GNU Linear Programming Kit) with simplex method based on exact arithmetic (using Sage backend)", "solver_name": "GLPK/exact"},
+ {"solver_brand_name": "COIN-BC (COIN Branch and Cut) (using Sage backend)", "solver_name": "Coin"},
+ {"solver_brand_name": "CVXOPT (Python Software for Convex Optimization) (using Sage backend)", "solver_name": "CVXOPT"},
+ {"solver_brand_name": "Gurobi Optimizer (using Sage backend)", "solver_name": "Gurobi"},
+ {"solver_brand_name": "PPL (Parma Polyhedra Library) (using Sage backend)", "solver_name": "PPL"},
+ {"solver_brand_name": "InteractiveLP (using Sage backend)", "solver_name": "InteractiveLP"},
+]
+
+MILP_SOLVERS_EXTERNAL = [
+ {
+ "solver_brand_name": "Gurobi Optimizer (external)",
+ "solver_name": "GUROBI_EXT",
+ "keywords": {
+ "command": {
+ "executable": "gurobi_cl",
+ "options": [],
+ "input_file": "",
+ "solve": "",
+ "output_file": "ResultFile=",
+ "end": "",
+ "format": ["executable", "output_file", "input_file"],
+ },
+ "time": r"Explored \d+ nodes \(\d+ simplex iterations\) in ([0-9]*[.]?[0-9]+) seconds",
+ "unsat_condition": "Model is infeasible",
+ },
+ },
+ {
+ "solver_brand_name": "GLPK (GNU Linear Programming Kit) (external)",
+ "solver_name": "GLPK_EXT",
+ "keywords": {
+ "command": {
+ "executable": "glpsol",
+ "options": ["--lp"],
+ "input_file": "",
+ "solve": "",
+ "output_file": "--output ",
+ "end": "",
+ "format": ["executable", "options", "input_file", "output_file"],
+ },
+ "time": r"Time used:[\s]+([0-9]*[.]?[0-9]+) secs",
+ "memory": r"Memory used:[\s]+([0-9]*[.]?[0-9]+) Mb",
+ "unsat_condition": r"PROBLEM HAS NO (\w+) FEASIBLE SOLUTION",
+ },
+ },
+ {
+ "solver_brand_name": "SCIP (Solving Constraint Integer Programs) (external)",
+ "solver_name": "SCIP_EXT",
+ "keywords": {
+ "command": {
+ "executable": "scip",
+ "options": ["-c", '"'],
+ "input_file": "read",
+ "solve": "optimize",
+ "output_file": "write solution",
+ "end": '"quit',
+ "format": [
+ "executable",
+ "options",
+ "input_file",
+ "solve",
+ "output_file",
+ "end",
+ ],
+ },
+ "time": r"Solving Time \(sec\)[\s]+:[\s]+([0-9]*[.]?[0-9]+)",
+ "unsat_condition": r"problem is solved \[infeasible\]",
+ },
+ },
+ {
+ "solver_brand_name": "IBM ILOG CPLEX Optimizer (external)",
+ "solver_name": "CPLEX_EXT",
+ "keywords": {
+ "command": {
+ "executable": "cplex",
+ "options": ["-c"],
+ "input_file": "read",
+ "solve": "optimize",
+ "output_file": "set logfile",
+ "end": "display solution variables -",
+ "format": [
+ "executable",
+ "options",
+ "input_file",
+ "solve",
+ "output_file",
+ "end",
+ ],
+ },
+ "time": r"Solution time =[\s]+([0-9]*[.]?[0-9]+) sec.",
+ "unsat_condition": "MIP - Integer infeasible.",
+ },
+ },
+]
diff --git a/claasp/cipher_modules/models/milp/utils/config.py b/claasp/cipher_modules/models/milp/utils/config.py
deleted file mode 100644
index a620edaa..00000000
--- a/claasp/cipher_modules/models/milp/utils/config.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import os
-
-# ****************************************************************************
-# Copyright 2023 Technology Innovation Institute
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-# ****************************************************************************
-
-
-SOLVER_DEFAULT = "GLPK"
-MODEL_DEFAULT_PATH = os.getcwd()
-SOLUTION_FILE_DEFAULT_NAME = "milp_model.sol"
-
-def get_external_milp_solver_configuration(solution_name=SOLUTION_FILE_DEFAULT_NAME):
-
- external_milp_solvers_configuration = {
- 'Gurobi': {
- 'command': f'gurobi_cl ResultFile={MODEL_DEFAULT_PATH}/{solution_name} ',
- 'options': "",
- 'time': r"Explored \d+ nodes \(\d+ simplex iterations\) in ([0-9]*[.]?[0-9]+) seconds",
- 'unsat_condition': "Model is infeasible"
- },
- 'scip': {
- 'file_path': [],
- 'command': 'scip -c \"read ',
- 'options': f' opt write solution {MODEL_DEFAULT_PATH}/{solution_name} quit\"',
- 'time': r"Solving Time \(sec\)[\s]+:[\s]+([0-9]*[.]?[0-9]+)",
- 'unsat_condition': "problem is solved [infeasible]"
- },
- 'glpk': {
- 'command': 'glpsol --lp ',
- 'options': f' --output {MODEL_DEFAULT_PATH}/{solution_name}',
- 'time': r"Time used:[\s]+([0-9]*[.]?[0-9]+) secs",
- 'memory': r"Memory used:[\s]+([0-9]*[.]?[0-9]+) Mb",
- 'unsat_condition': 'PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION'
- },
- 'cplex': {
- 'command': 'cplex -c \"read ',
- 'options': f'\" \"optimize\" \"display solution variables *\" | tee {MODEL_DEFAULT_PATH}/{solution_name}',
- 'time': r"Solution time =[\s]+([0-9]*[.]?[0-9]+) sec.",
- 'unsat_condition': "MIP - Integer infeasible."
- }
- }
-
- for solver in external_milp_solvers_configuration:
- external_milp_solvers_configuration[solver]['solution_path'] = f'{MODEL_DEFAULT_PATH}/{solution_name}'
-
- return external_milp_solvers_configuration
diff --git a/claasp/cipher_modules/models/milp/utils/generate_inequalities_for_and_operation_2_input_bits.py b/claasp/cipher_modules/models/milp/utils/generate_inequalities_for_and_operation_2_input_bits.py
index 5a720ddf..5af3f6af 100644
--- a/claasp/cipher_modules/models/milp/utils/generate_inequalities_for_and_operation_2_input_bits.py
+++ b/claasp/cipher_modules/models/milp/utils/generate_inequalities_for_and_operation_2_input_bits.py
@@ -18,7 +18,7 @@
"""The target of this module is to generate MILP inequalities for a AND operation between 2 input bits."""
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT
def and_inequalities():
diff --git a/claasp/cipher_modules/models/milp/utils/generate_sbox_inequalities_for_trail_search.py b/claasp/cipher_modules/models/milp/utils/generate_sbox_inequalities_for_trail_search.py
index ece6f1ef..8cfc2340 100644
--- a/claasp/cipher_modules/models/milp/utils/generate_sbox_inequalities_for_trail_search.py
+++ b/claasp/cipher_modules/models/milp/utils/generate_sbox_inequalities_for_trail_search.py
@@ -30,7 +30,7 @@
from claasp.cipher_modules.models.milp import MILP_AUXILIARY_FILE_PATH
from sage.rings.integer_ring import ZZ
-from claasp.cipher_modules.models.milp.utils.config import SOLVER_DEFAULT
+from claasp.cipher_modules.models.milp.solvers import SOLVER_DEFAULT
small_sbox_file_name = "dictionary_that_contains_inequalities_for_small_sboxes.obj"
small_sbox_xor_linear_file_name = "dictionary_that_contains_inequalities_for_small_sboxes_xor_linear.obj"
diff --git a/claasp/cipher_modules/models/milp/utils/utils.py b/claasp/cipher_modules/models/milp/utils/utils.py
index 3101c25e..c4b0b77c 100644
--- a/claasp/cipher_modules/models/milp/utils/utils.py
+++ b/claasp/cipher_modules/models/milp/utils/utils.py
@@ -25,7 +25,6 @@
output_dictionary_that_contains_xor_inequalities,
update_dictionary_that_contains_xor_inequalities_between_n_input_bits)
-from claasp.cipher_modules.models.milp.utils.config import get_external_milp_solver_configuration
from sage.numerical.mip import MIPSolverException
from claasp.cipher_modules.models.milp.utils.milp_name_mappings import MILP_BITWISE_DETERMINISTIC_TRUNCATED, \
@@ -62,20 +61,16 @@ def _get_variables_value(internal_variables, read_file):
return variables_value
-def _parse_external_solver_output(model, solvers_configurations, model_type, solver_name, solver_process):
- solver_specs = solvers_configurations[solver_name]
-
- solve_time = _get_data(solver_specs['time'], str(solver_process))
+def _parse_external_solver_output(model, solver_specs, model_type, solution_file_path, solver_process):
+ solve_time = _get_data(solver_specs['keywords']['time'], solver_process)
status = 'UNSATISFIABLE'
objective_value = None
components_values = None
- if solver_specs['unsat_condition'] not in str(solver_process):
+ if re.findall(solver_specs['keywords']['unsat_condition'], solver_process) == []:
status = 'SATISFIABLE'
- solution_file_path = solver_specs['solution_path']
-
with open(solution_file_path, 'r') as lp_file:
read_file = lp_file.read()
diff --git a/claasp/cipher_modules/report.py b/claasp/cipher_modules/report.py
index 345dad89..c2fa5b9d 100644
--- a/claasp/cipher_modules/report.py
+++ b/claasp/cipher_modules/report.py
@@ -4,11 +4,13 @@
from plotly import express as px
import plotly.graph_objects as go
import pandas as pd
+import itertools
import json
import shutil
from claasp.cipher_modules.statistical_tests.dieharder_statistical_tests import DieharderTests
from claasp.cipher_modules.statistical_tests.nist_statistical_tests import NISTStatisticalTests
from claasp.cipher_modules.component_analysis_tests import CipherComponentsAnalysis
+from datetime import datetime
def _print_colored_state(state, verbose, file):
@@ -17,11 +19,11 @@ def _print_colored_state(state, verbose, file):
for x in line:
print(f'{x} ', end='', file=file)
- occ = [i for i in range(len(line)) if line[i] != '_' and line[i] != '*']
+ occ = [i for i in range(len(line)) if line[i] != '_' and line[i] != '*' and line[i] != '0']
if verbose:
print(f'\tactive words positions = {occ}', file=file)
else:
- print('', file=file)
+ print('', end=' ', file=file)
def _dict_to_latex_table(data_dict, header_list):
@@ -151,7 +153,7 @@ def __init__(self, test_report):
self.input_parameters = {}
self.test_name = test_report['test_name'] if type(test_report) is dict else test_report[0]['test_name']
- def show(self, test_name=None, fixed_input='plaintext', fixed_output='round_output',
+ def show(self, show_as_hex=False, test_name=None, fixed_input='plaintext', fixed_output='round_output',
fixed_input_difference='average', word_size=1, state_size=1, key_state_size=1,
verbose=False, show_word_permutation=False,
show_var_shift=False, show_var_rotate=False, show_theta_xoodoo=False,
@@ -165,7 +167,10 @@ def show(self, test_name=None, fixed_input='plaintext', fixed_output='round_outp
show_intermediate_output=True, show_cipher_output=True, show_input=True, show_output=True):
if 'trail' in self.test_name:
- self._print_trail(word_size, state_size, key_state_size, verbose, show_word_permutation,
+ if show_as_hex == True and (word_size / 4).is_integer() == False:
+ print("Incorrect word_size: if show_as_hex=True, word_size has to be a multiple of 4")
+ return
+ self._print_trail(show_as_hex, word_size, state_size, key_state_size, verbose, show_word_permutation,
show_var_shift, show_var_rotate, show_theta_xoodoo,
show_theta_keccak, show_shift_rows, show_sigma, show_reverse,
show_permuation, show_multi_input_non_linear_logical_operator,
@@ -192,7 +197,7 @@ def show(self, test_name=None, fixed_input='plaintext', fixed_output='round_outp
'input_difference_value' in x.keys()]
if fixed_input_difference not in input_diff_values:
print(
- 'Error! Invalid input difference value. The report.show() function requires an input_difference_value input')
+ 'Error! Invalid input difference value. The report.show() function requires a fixed_input_difference input')
print('input_difference_value has to be one of the following :', end='')
print(input_diff_values)
return
@@ -202,14 +207,14 @@ def show(self, test_name=None, fixed_input='plaintext', fixed_output='round_outp
'neural_network_differential_distinguisher']]
if fixed_input_difference not in input_diff_values:
print(
- 'Error! Invalid input difference value. The report.show() function requires an input_difference_value input')
+ 'Error! Invalid input difference value. The report.show() function requires a fixed_input_difference input')
print('The input difference value has to be one of the following :', end='')
print(input_diff_values)
return
self._produce_graph(show_graph=True, fixed_input=fixed_input, fixed_output=fixed_output,
fixed_input_difference=fixed_input_difference, test_name=test_name)
- def _export(self, file_format, output_dir, fixed_input = None, fixed_output=None, fixed_test = None):
+ def _export(self, file_format, output_dir, fixed_input=None, fixed_output=None, fixed_test=None):
if not os.path.exists(output_dir):
os.makedirs(output_dir)
@@ -290,7 +295,8 @@ def _export(self, file_format, output_dir, fixed_input = None, fixed_output=None
os.makedirs(output_dir + '/' + self.cipher.id + '/' + self.test_report["input_parameters"][
"test_name"] + '_tables/' + it + '/' + out)
- for test in self.test_report["test_results"][it][out].keys() if fixed_test == None else [fixed_test]:
+ for test in self.test_report["test_results"][it][out].keys() if fixed_test == None else [
+ fixed_test]:
if not os.path.exists(
output_dir + '/' + self.cipher.id + '/' + self.test_report["input_parameters"][
"test_name"] + '_tables/' + it + '/' + out + '/' + test):
@@ -395,16 +401,190 @@ def _export(self, file_format, output_dir, fixed_input = None, fixed_output=None
print("Report saved in " + output_dir + '/' + self.cipher.id)
- def save_as_DataFrame(self, output_dir=os.getcwd() + '/test_reports', fixed_input = None, fixed_output=None, fixed_test=None):
- self._export(file_format='.csv', output_dir=output_dir, fixed_input=fixed_input, fixed_output=fixed_output, fixed_test = fixed_test)
+ def save_as_DataFrame(self, output_dir=os.getcwd() + '/test_reports', fixed_input=None, fixed_output=None,
+ fixed_test=None):
+ self._export(file_format='.csv', output_dir=output_dir, fixed_input=fixed_input, fixed_output=fixed_output,
+ fixed_test=fixed_test)
+
+ def save_as_latex_table(self, output_dir=os.getcwd() + '/test_reports', fixed_input=None, fixed_output=None,
+ fixed_test=None):
+ self._export(file_format='.tex', output_dir=output_dir, fixed_input=fixed_input, fixed_output=fixed_output,
+ fixed_test=fixed_test)
+
+ def save_as_json(self, output_dir=os.getcwd() + '/test_reports', fixed_input=None, fixed_output=None,
+ fixed_test=None):
+ self._export(file_format='.json', output_dir=output_dir, fixed_input=fixed_input, fixed_output=fixed_output,
+ fixed_test=fixed_test)
+
+ def _get_component_types(self):
+ component_types = []
+ show_key_flow = False
+ for comp in list(self.test_report['components_values'].keys()):
+ if 'key' in comp:
+ show_key_flow = True
+ if ('key' in comp or comp == 'plaintext') and comp not in component_types:
+ component_types.append(comp)
+ elif '_'.join(comp.split('_')[:-2]) not in component_types and comp[-2:] != "_i" and comp[-2:] != "_o":
+ component_types.append('_'.join(comp.split('_')[:-2]))
+ elif ('_'.join(comp.split('_')[:-3])) + '_' + ('_'.join(comp.split('_')[-1])) not in component_types and (
+ comp[-2:] == "_i" or comp[-2:] == "_o"):
+ component_types.append(('_'.join(comp.split('_')[:-3])) + '_' + ('_'.join(comp.split('_')[-1])))
+
+ return component_types, show_key_flow
+
+ def _get_show_components(self, component_types, show_output, show_input, show_key, input_comps, var_choices):
+
+ show_components = {}
+ for comp, comp_choice in itertools.product(component_types, input_comps):
+ if 'show_' + comp == comp_choice:
+ show_components[comp] = var_choices[comp_choice]
+ elif 'show_' + comp == comp_choice + '_o' and show_output:
+ show_components[comp] = var_choices[comp_choice]
+ elif 'show_' + comp == comp_choice + '_i' and show_input:
+ show_components[comp] = var_choices[comp_choice]
+
+ return show_components
+
+ def _update_out_list(self, out_list, rel_prob, abs_prob, show_as_hex, comp_id, word_size, state_size,
+ key_state_size, key_flow, word_denominator):
+
+ 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(
+ value[2:])
+
+ if show_as_hex == False:
+ 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
+ range(0, len(bin_list), word_size)]
+ else:
+ word_list = [
+ '*' if '*' in ''.join(bin_list[x:x + word_size]) else hex(int(''.join(bin_list[x:x + word_size]), 2))[
+ 2:].zfill(int(word_size/4)) for x
+ in range(0, len(bin_list), word_size)]
+
+ if ('intermediate' in comp_id or 'cipher' in comp_id) and comp_id not in key_flow:
+ size = (state_size, len(word_list) // state_size)
+
+ elif ('intermediate' in comp_id or 'cipher' in comp_id) and comp_id in key_flow and comp_id != 'key':
+ size = (key_state_size, len(word_list) // key_state_size)
+
+ else:
+ size = (1, len(word_list))
+ out_format = [[] for _ in range(size[0])]
+ for i, j in itertools.product(range(size[0]), range(size[1])):
+ if show_as_hex == False:
+ if word_list[j + i * size[1]] == word_denominator:
+ out_format[i].append(f'\033[31;4m{word_list[j + i * size[1]]}\033[0m')
+ else:
+ out_format[i].append(word_list[j + i * size[1]])
+ else:
+ if word_list[j + i * size[1]] == '0':
+ out_format[i].append(word_list[j + i * size[1]])
+ else:
+ out_format[i].append(f'\033[31;4m{word_list[j + i * size[1]]}\033[0m')
- def save_as_latex_table(self, output_dir=os.getcwd() + '/test_reports', fixed_input = None, fixed_output=None, fixed_test=None):
- self._export(file_format='.tex', output_dir=output_dir, fixed_input=fixed_input, fixed_output=fixed_output, fixed_test = fixed_test)
+ out_list[comp_id] = (out_format, rel_prob, abs_prob) if comp_id not in ["plaintext", "key"] else (
+ out_format, 0, 0)
- def save_as_json(self, output_dir=os.getcwd() + '/test_reports', fixed_input = None, fixed_output=None, fixed_test=None):
- self._export(file_format='.json', output_dir=output_dir, fixed_input=fixed_input, fixed_output=fixed_output, fixed_test = fixed_test)
+ def _get_comp_value_and_key_flow(self, comp_id, key_flow):
+
+ if comp_id[-2:] == "_i" or comp_id[-2:] == "_o":
+ input_links = self.cipher.get_component_from_id(comp_id[:-2]).input_id_links
+ comp_value = ('_'.join(comp_id.split('_')[:-3])) + '_' + ('_'.join(comp_id.split('_')[-1]))
+ else:
+ input_links = self.cipher.get_component_from_id(comp_id).input_id_links
+ comp_value = '_'.join(comp_id.split('_')[:-2])
+
+ if (all(
+ id_link in key_flow or 'constant' in id_link or id_link + '_o' in key_flow or id_link + '_i' in key_flow
+ for id_link in input_links) or ('key' in comp_id and comp_id != 'key')):
+ key_flow.append(comp_id)
+ if 'linear' in self.test_name:
+ constants_i = [constant_id + '_i' for constant_id in input_links if 'constant' in constant_id]
+ constants_o = [constant_id + '_o' for constant_id in input_links if 'constant' in constant_id]
+ key_flow += constants_i + constants_o
+ else:
+ constants = [constant_id for constant_id in input_links if 'constant' in constant_id]
+ key_flow += constants
+ return comp_value, key_flow
+
+ def _get_show_key_flow(self, key_flow, word_size, word_denominator):
+ show_key_flow = False
+ for key_comp in key_flow:
+ key_value = self.test_report['components_values'][key_comp]['value']
+ bin_list = list(format(int(key_value, 16), 'b').zfill(
+ 4 * len(key_value) if key_value[:2] != '0x' else 4 * len(
+ key_value[2:]))) if '*' not in key_value else list(
+ key_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
+ range(0, len(bin_list), word_size)]
+
+ if word_list.count(word_denominator) > 0:
+ show_key_flow = True
+ break
+ return show_key_flow
+
+ def _print_plaintext_flow(self, out_list, key_flow, verbose, file, save_fig):
+ for comp_id in [comp for comp in out_list.keys() if comp not in key_flow]:
+ if comp_id == 'plaintext':
+ if verbose == True:
+ print(f'{comp_id}\t', file=file)
+ _print_colored_state(out_list[comp_id][0], verbose, file)
+ else:
+ _print_colored_state(out_list[comp_id][0], verbose, file)
+ print(f' {comp_id}\t', file=file)
+ else:
+ if verbose:
+ print(
+ f'{comp_id} Input Links : {self.cipher.get_component_from_id(comp_id if comp_id[-2:] not in ["_i", "_o"] else comp_id[:-2]).input_id_links}',
+ file=file if save_fig else None)
+ _print_colored_state(out_list[comp_id][0], verbose, file)
+ else:
+ _print_colored_state(out_list[comp_id][0], verbose, file)
+ print(f' {comp_id}', file=file)
+ if verbose:
+ print('local weight = ' + str(
+ out_list[comp_id][1]) + '\t' + 'total weight = ' + str(
+ out_list[comp_id][2]), file=file)
+ print('', file=file)
+ print('', file=file)
+ print('total weight = ' + str(self.test_report['total_weight']), file=file)
+
+ def _print_key_flow(self, key_flow, show_components, out_list, verbose, file):
+
+ print('', file=file)
+ print("KEY FLOW", file=file)
+ print('', file=file)
+
+ for comp_id in key_flow:
+ if comp_id[-2:] == "_i" or comp_id[-2:] == "_o":
+ comp_value = ('_'.join(comp_id.split('_')[:-3])) + '_' + ('_'.join(comp_id.split('_')[-1]))
+ else:
+ comp_value = '_'.join(comp_id.split('_')[:-2])
+ if show_components[
+ comp_value if (comp_id not in ['plaintext', 'cipher_output', 'cipher_output_o', 'cipher_output_i',
+ 'intermediate_output', 'intermediate_output_o',
+ 'intermediate_output_i'] and 'key' not in comp_id) else comp_id]:
+ if 'key' in comp_id:
+ _print_colored_state(out_list[comp_id][0], verbose, file)
+ print(f'\t{comp_id}\t', file=file)
+ else:
+ if verbose:
+ print(
+ f'{comp_id} Input Links : {self.cipher.get_component_from_id(comp_id if comp_id[-2:] not in ["_i", "_o"] else comp_id[:-2]).input_id_links}',
+ file=file)
+ _print_colored_state(out_list[comp_id][0], verbose, file)
+ else:
+ _print_colored_state(out_list[comp_id][0], verbose, file)
+ print(f'{comp_id}\t', file=file)
+ if verbose: print('local weight = ' + str(
+ out_list[comp_id][1]) + '\t' + 'total weight = ' + str(
+ out_list[comp_id][2]), file=file)
+ print('', file=file)
- def _print_trail(self, word_size, state_size, key_state_size, verbose, show_word_permutation,
+ def _print_trail(self, show_as_hex, word_size, state_size, key_state_size, verbose, show_word_permutation,
show_var_shift, show_var_rotate, show_theta_xoodoo,
show_theta_keccak, show_shift_rows, show_sigma, show_reverse,
show_permuation, show_multi_input_non_linear_logical_operator,
@@ -429,33 +609,14 @@ def _print_trail(self, word_size, state_size, key_state_size, verbose, show_word
file = None
input_comps = list(locals().keys())
- component_types = []
- for comp in list(self.test_report['components_values'].keys()):
- if 'key' in comp:
- show_key_flow = True
- if ('key' in comp or comp == 'plaintext') and comp not in component_types:
- component_types.append(comp)
- elif '_'.join(comp.split('_')[:-2]) not in component_types and comp[-2:] != "_i" and comp[-2:] != "_o":
- component_types.append('_'.join(comp.split('_')[:-2]))
- elif ('_'.join(comp.split('_')[:-3])) + '_' + ('_'.join(comp.split('_')[-1])) not in component_types and (
- comp[-2:] == "_i" or comp[-2:] == "_o"):
- component_types.append(('_'.join(comp.split('_')[:-3])) + '_' + ('_'.join(comp.split('_')[-1])))
+ component_types, show_key_flow = self._get_component_types()
+ show_components = self._get_show_components(component_types, show_output, show_input, show_key, input_comps,
+ locals())
- show_components = {}
- for comp in component_types:
- for comp_choice in input_comps:
- if 'show_' + comp == comp_choice:
- show_components[comp] = locals()[comp_choice]
- if 'show_' + comp == comp_choice + '_o' and show_output:
- show_components[comp] = locals()[comp_choice]
- if 'show_' + comp == comp_choice + '_i' and show_input:
- show_components[comp] = locals()[comp_choice]
- if 'key' in comp and show_key == True:
- show_components[comp] = True
out_list = {}
- key_flow = ['key']
+ key_flow = [key for key in self.cipher.inputs if key == 'key']
abs_prob = 0
rel_prob = 0
@@ -468,121 +629,22 @@ def _print_trail(self, word_size, state_size, key_state_size, verbose, show_word
abs_prob += rel_prob
# Check input links
-
- if 'plaintext' not in comp_id and 'key' not in comp_id:
- if comp_id[-2:] == "_i" or comp_id[-2:] == "_o":
- input_links = self.cipher.get_component_from_id(comp_id[:-2]).input_id_links
- comp_value = ('_'.join(comp_id.split('_')[:-3])) + '_' + ('_'.join(comp_id.split('_')[-1]))
- else:
- input_links = self.cipher.get_component_from_id(comp_id).input_id_links
- comp_value = '_'.join(comp_id.split('_')[:-2])
-
- if (all((
- id_link in key_flow or 'constant' in id_link or id_link + '_o' in key_flow or id_link + '_i' in key_flow)
- for id_link in input_links) or ('key' in comp_id and 'comp_id' != 'key')):
- key_flow.append(comp_id)
- if 'linear' in self.test_name:
- constants_i = [constant_id + '_i' for constant_id in input_links if 'constant' in constant_id]
- constants_o = [constant_id + '_o' for constant_id in input_links if 'constant' in constant_id]
- key_flow = key_flow + constants_i + constants_o
- else:
- constants = [constant_id for constant_id in input_links if 'constant' in constant_id]
- key_flow = key_flow + constants
+ if comp_id != 'plaintext' and 'key' not in comp_id:
+ comp_value, key_flow = self._get_comp_value_and_key_flow(comp_id, key_flow)
if show_components[
comp_value if (comp_id not in ['plaintext', 'cipher_output', 'cipher_output_o', 'cipher_output_i',
'intermediate_output', 'intermediate_output_o',
'intermediate_output_i'] and 'key' not in comp_id) else comp_id]:
+ self._update_out_list(out_list, rel_prob, abs_prob, show_as_hex, comp_id, word_size, state_size,
+ key_state_size, key_flow, word_denominator)
- 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(
- 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
- range(0, len(bin_list), word_size)]
+ self._print_plaintext_flow(out_list, key_flow, verbose, file, save_fig)
- if ('intermediate' in comp_id or 'cipher' in comp_id) and comp_id not in key_flow:
- size = (state_size, len(word_list) // state_size)
-
- elif ('intermediate' in comp_id or 'cipher' in comp_id) and comp_id in key_flow and comp_id != 'key':
- size = (key_state_size, len(word_list) // key_state_size)
-
- else:
- size = (1, len(word_list))
- out_format = [[] for _ in range(size[0])]
- for i in range(size[0]):
- for j in range(size[1]):
- if word_list[j + i * size[1]] == word_denominator:
- out_format[i].append(f'\033[31;4m{word_list[j + i * size[1]]}\033[0m')
- else:
- out_format[i].append(word_list[j + i * size[1]])
-
- out_list[comp_id] = (out_format, rel_prob, abs_prob) if comp_id not in ["plaintext", "key"] else (
- out_format, 0, 0)
-
- for comp_id in out_list.keys():
- if comp_id not in key_flow:
- if comp_id == 'plaintext' or 'key' in comp_id:
- print(f'\t{comp_id}\t', file=file)
- else:
- if verbose:
- print(
- f' \t{comp_id} Input Links : {self.cipher.get_component_from_id(comp_id if comp_id[-2:] not in ["_i", "_o"] else comp_id[:-2]).input_id_links}',
- file=file if save_fig else None)
- else:
- print(f' \t{comp_id}\t', file=file)
- _print_colored_state(out_list[comp_id][0], verbose, file)
- if verbose: print(' ' * len(out_list[comp_id][0][0]) + '\tlocal weight = ' + str(
- out_list[comp_id][1]) + '\t' + 'total weight = ' + str(
- out_list[comp_id][2]), file=file)
- print('', file=file)
-
- print('', file=file)
- print('total weight = ' + str(self.test_report['total_weight']), file=file)
- show_key_flow = False
-
- for key_comp in key_flow:
- if key_comp != 'key' and self.test_report['components_values'][key_comp]['weight'] != 0:
- show_key_flow = True
- break
+ show_key_flow = self._get_show_key_flow(key_flow, word_size, word_denominator)
if show_key_flow:
- print('', file=file)
- print("KEY FLOW", file=file)
- print('', file=file)
-
- for comp_id in key_flow:
- if comp_id not in ['plaintext', 'key', 'cipher_output',
- 'intermediate_output'] and 'key' not in comp_id and 'intermediate_output' not in comp_id and comp_id[
- -2:] not in [
- '_i', '_o']:
- identifier = '_'.join(comp_id.split('_')[:-2])
- elif 'intermediate_output' in comp_id:
- identifier = 'intermediate_output' + comp_id[-2:]
- elif comp_id[-2:] == '_i' and show_input:
- identifier = comp_id.split('_')[0] + '_i'
- elif comp_id[-2:] == '_o' and show_output:
- identifier = comp_id.split('_')[0] + '_o'
- else:
- identifier = comp_id
- if show_components[identifier]:
- if comp_id == 'plaintext' or 'key' in comp_id:
- print(f'\t{comp_id}\t', file=file)
- else:
- if verbose:
- print(
- f' \t{comp_id} Input Links : {self.cipher.get_component_from_id(comp_id if comp_id[-2:] not in ["_i", "_o"] else comp_id[:-2]).input_id_links}',
- file=file)
- else:
- print(f' \t{comp_id}\t', file=file)
- _print_colored_state(out_list[comp_id][0], verbose, file)
- if verbose: print(' ' * len(out_list[comp_id][0][0]) + '\tlocal weight = ' + str(
- out_list[comp_id][1]) + '\t' + 'total weight = ' + str(
- out_list[comp_id][2]), file=file)
- print('', file=file)
+ self._print_key_flow(key_flow, show_components, out_list, verbose, file)
def _produce_graph(self, output_directory=os.getcwd(), show_graph=False, fixed_input=None, fixed_output=None,
fixed_input_difference=None, test_name=None):
@@ -624,19 +686,19 @@ def _produce_graph(self, output_directory=os.getcwd(), show_graph=False, fixed_i
if 'dieharder' in self.test_name:
for dict in self.test_report['test_results']:
DieharderTests._generate_chart_round(dict,
- output_directory + '/' + self.cipher.id + '/' + self.test_name,
+ output_directory,
show_graph=True)
DieharderTests._generate_chart_all(self.test_report['test_results'],
- output_directory + '/' + self.cipher.id + '/' + self.test_name,
+ output_directory,
show_graph=True)
elif 'nist' in self.test_name:
for dict in self.test_report['test_results']:
NISTStatisticalTests._generate_chart_round(dict,
- output_directory + '/' + self.cipher.id + '/' + self.test_name,
+ output_directory,
show_graph=True)
NISTStatisticalTests._generate_chart_all(self.test_report['test_results'],
- output_directory + '/' + self.cipher.id + '/' + self.test_name,
+ output_directory,
show_graph=True)
elif 'algebraic' in self.test_name:
@@ -652,11 +714,10 @@ def _produce_graph(self, output_directory=os.getcwd(), show_graph=False, fixed_i
fig.update_traces(text=z_text, texttemplate="%{text}")
fig.update(layout_coloraxis_showscale=False)
fig.update_xaxes(side="top")
- print(output_directory)
if show_graph == False:
print('saving image')
- fig.write_image(output_directory + '/' + self.cipher.id + '/' + self.test_name + '/test_results.png')
+ fig.write_image(output_directory + '/test_results.png')
print('image saved')
if show_graph:
fig.show(renderer='png')
@@ -666,24 +727,24 @@ def _produce_graph(self, output_directory=os.getcwd(), show_graph=False, fixed_i
inputs = list(self.test_report['test_results'].keys())
for it in inputs if fixed_input == None else [fixed_input]:
if not os.path.exists(
- output_directory + '/' + self.cipher.id + '/' + self.test_name + '/' + it) and show_graph == False:
- os.mkdir(output_directory + '/' + self.cipher.id + '/' + self.test_name + '/' + it)
+ output_directory + '/' + it) and show_graph == False:
+ os.mkdir(output_directory + '/' + it)
outputs = list(self.test_report['test_results'][it].keys())
for out in outputs if fixed_input == None else [fixed_output]:
if out == 'cipher_output':
continue
if not os.path.exists(
- output_directory + '/' + self.cipher.id + '/' + self.test_name + '/' + it + '/' + out) and show_graph == False:
+ output_directory + '/' + it + '/' + out) and show_graph == False:
os.mkdir(
- output_directory + '/' + self.cipher.id + '/' + self.test_name + '/' + it + '/' + out)
+ output_directory + '/' + it + '/' + out)
results = list(self.test_report['test_results'][it][out].keys())
for res in results if test_name == None else [test_name]:
- if not os.path.exists(output_directory + '/' +
- self.cipher.id + '/' + self.test_name + '/' + it + '/' + out + '/' + res) and show_graph == False:
+ if not os.path.exists(
+ output_directory + '/' + it + '/' + out + '/' + res) and show_graph == False:
os.mkdir(
- output_directory + '/' + self.cipher.id + '/' + self.test_name + '/' + it + '/' + out + '/' + res)
+ output_directory + '/' + it + '/' + out + '/' + res)
### Make Graphs
@@ -742,8 +803,7 @@ def _produce_graph(self, output_directory=os.getcwd(), show_graph=False, fixed_i
})
if show_graph == False:
- fig.write_image(output_directory + '/' +
- self.cipher.id + '/' + self.test_name + '/' + it + '/' + out + '/' + res + '/' + str(
+ fig.write_image(output_directory + '/' + it + '/' + out + '/' + res + '/' + str(
res) + '_' + str(case['input_difference_value']) + '.png', scale=4)
else:
fig.show(renderer='png')
@@ -759,8 +819,7 @@ def _produce_graph(self, output_directory=os.getcwd(), show_graph=False, fixed_i
showlegend=False)
if show_graph == False:
- fig.write_image(output_directory + '/' +
- self.cipher.id + '/' + self.test_name + '/' + it + '/' + out + '/' + res +
+ fig.write_image(output_directory + '/' + it + '/' + out + '/' + res +
'/' + str(res) + '.png',
scale=4)
else:
@@ -769,8 +828,9 @@ def _produce_graph(self, output_directory=os.getcwd(), show_graph=False, fixed_i
fig.data = []
fig.layout = {}
- def save_as_image(self, test_name=None, fixed_input=None, fixed_output=None,
- fixed_input_difference=None, word_size=1, state_size=1, key_state_size=1, output_directory=os.getcwd() + '/test_reports',
+ def save_as_image(self, show_as_hex=False, test_name=None, fixed_input=None, fixed_output=None,
+ fixed_input_difference=None, word_size=1, state_size=1, key_state_size=1,
+ output_directory=os.getcwd() + '/test_reports',
verbose=False, show_word_permutation=False,
show_var_shift=False, show_var_rotate=False, show_theta_xoodoo=False,
show_theta_keccak=False, show_shift_rows=False, show_sigma=False, show_reverse=False,
@@ -804,12 +864,13 @@ def save_as_image(self, test_name=None, fixed_input=None, fixed_output=None,
sage: report.save_as_image()
"""
-
+ time = '_date:' + 'time:'.join(str(datetime.now()).split(' '))
+ test_directory = output_directory
if 'component_analysis' in self.test_name:
print('This method is not implemented yet for the component analysis test')
return
elif 'trail' in self.test_name:
- self._print_trail(word_size, state_size, key_state_size, verbose, show_word_permutation,
+ self._print_trail(show_as_hex, word_size, state_size, key_state_size, verbose, show_word_permutation,
show_var_shift, show_var_rotate, show_theta_xoodoo,
show_theta_keccak, show_shift_rows, show_sigma, show_reverse,
show_permuation, show_multi_input_non_linear_logical_operator,
@@ -823,13 +884,16 @@ def save_as_image(self, test_name=None, fixed_input=None, fixed_output=None,
if not os.path.exists(output_directory):
os.mkdir(output_directory)
- if not os.path.exists(output_directory + '/' + self.cipher.id):
- os.mkdir(output_directory + '/' + self.cipher.id)
+ if not os.path.exists(output_directory + '/' + self.cipher.id + time):
+ os.mkdir(output_directory + '/' + self.cipher.id + time)
+
+ if not os.path.exists(output_directory + '/' + self.cipher.id + time + '/' + self.test_name):
+ os.mkdir(output_directory + '/' + self.cipher.id + time + '/' + self.test_name)
- if not os.path.exists(output_directory + '/' + self.cipher.id + '/' + self.test_name):
- os.mkdir(output_directory + '/' + self.cipher.id + '/' + self.test_name)
- self._produce_graph(output_directory, test_name=test_name, fixed_output=fixed_output, fixed_input_difference=fixed_input_difference, fixed_input=fixed_input)
- print('Report saved in ' + output_directory)
+ test_directory = output_directory + '/' + self.cipher.id + time + '/' + self.test_name
+ self._produce_graph(output_directory=test_directory, test_name=test_name, fixed_output=fixed_output,
+ fixed_input_difference=fixed_input_difference, fixed_input=fixed_input)
+ print('Report saved in ' + test_directory)
def clean_reports(self, output_dir=os.getcwd() + '/test_reports'):
diff --git a/claasp/cipher_modules/statistical_tests/dieharder_statistical_tests.py b/claasp/cipher_modules/statistical_tests/dieharder_statistical_tests.py
index 433fe010..a55ed398 100644
--- a/claasp/cipher_modules/statistical_tests/dieharder_statistical_tests.py
+++ b/claasp/cipher_modules/statistical_tests/dieharder_statistical_tests.py
@@ -19,7 +19,7 @@
import os
import math
import time
-from datetime import timedelta
+from datetime import timedelta, datetime
import matplotlib.pyplot as plt
from claasp.cipher_modules.statistical_tests.dataset_generator import DatasetGenerator, DatasetType
@@ -455,8 +455,9 @@ def _generate_chart_all(report_dict_list, output_dir='', show_graph=False):
print(f'Drawing chart for all rounds is in finished.')
def _create_report_folder(self):
+ time_date = 'date:'+'time:'.join(str(datetime.now()).split(' '))
self.report_folder = os.path.join(self.folder_prefix,
- f'{self._cipher_primitive}_{self.dataset_type_dieharder.name}_index{self.input_index}_{self.number_of_sequences_dieharder}lines_{self.bits_in_one_sequence_dieharder}bits')
+ f'{self._cipher_primitive}_{self.dataset_type_dieharder.name}_index{self.input_index}_{self.number_of_sequences_dieharder}lines_{self.bits_in_one_sequence_dieharder}bits_{time_date}time')
try:
os.makedirs(self.report_folder)
except OSError:
@@ -471,7 +472,7 @@ def _write_execution_time(self, execution_description, execution_time):
print(f'Error: {e.strerror}')
def _generate_dieharder_dicts(self, dataset, round_start, round_end, dieharder_test_option, FLAG_CHART=False):
- dataset_folder = os.getcwd() + '/dataset'
+ dataset_folder = os.getcwd() + 'dieharder_dataset'
dataset_filename = 'dieharder_input_' + self._cipher_primitive
dataset_filename = os.path.join(dataset_folder, dataset_filename)
dieharder_report_dicts = []
diff --git a/claasp/cipher_modules/statistical_tests/nist_statistical_tests.py b/claasp/cipher_modules/statistical_tests/nist_statistical_tests.py
index 9b6d42e5..1b2abceb 100644
--- a/claasp/cipher_modules/statistical_tests/nist_statistical_tests.py
+++ b/claasp/cipher_modules/statistical_tests/nist_statistical_tests.py
@@ -21,12 +21,11 @@
import time
import shutil
import pathlib
-from datetime import timedelta
+from datetime import timedelta, datetime
import matplotlib.pyplot as plt
from claasp.cipher_modules.statistical_tests.dataset_generator import DatasetGenerator, DatasetType
-reports_path = "test_reports/statistical_tests/nist_statistics_report"
TEST_ID_TABLE = {
@@ -95,6 +94,7 @@ def nist_statistical_tests(self, test_type,
"""
+ time_date = 'date:'+'time:'.join(str(datetime.now()).split(' '))
nist_test = {
'input_parameters': {
@@ -132,7 +132,7 @@ def nist_statistical_tests(self, test_type,
self.number_of_samples = self.number_of_samples_in_one_sequence * (self.number_of_sequences + 1)
self.bits_in_one_sequence = sample_size * self.number_of_samples_in_one_sequence
- self._create_report_folder(statistical_test_option_list)
+ self._create_report_folder(time_date,statistical_test_option_list)
dataset = self.data_generator.generate_avalanche_dataset(input_index=self.input_index,
number_of_samples=self.number_of_samples)
@@ -151,7 +151,7 @@ def nist_statistical_tests(self, test_type,
self.number_of_sequences = number_of_sequences
self.number_of_samples = self.number_of_sequences + 1
self.bits_in_one_sequence = number_of_blocks_in_one_sample * self.cipher.output_bit_size
- self._create_report_folder(statistical_test_option_list)
+ self._create_report_folder(time_date,statistical_test_option_list)
dataset = self.data_generator.generate_correlation_dataset(input_index=self.input_index,
number_of_samples=self.number_of_samples,
@@ -170,7 +170,7 @@ def nist_statistical_tests(self, test_type,
self.number_of_sequences = number_of_sequences
self.number_of_samples = self.number_of_sequences + 1
self.bits_in_one_sequence = number_of_blocks_in_one_sample * self.cipher.output_bit_size
- self._create_report_folder(statistical_test_option_list)
+ self._create_report_folder(time_date,statistical_test_option_list)
dataset = self.data_generator.generate_cbc_dataset(input_index=self.input_index,
number_of_samples=self.number_of_samples,
@@ -188,7 +188,7 @@ def nist_statistical_tests(self, test_type,
self.number_of_sequences = number_of_sequences
self.number_of_samples = self.number_of_sequences + 1
self.bits_in_one_sequence = self.number_of_blocks_in_one_sample * self.cipher.output_bit_size
- self._create_report_folder(statistical_test_option_list)
+ self._create_report_folder(time_date,statistical_test_option_list)
dataset = self.data_generator.generate_random_dataset(input_index=self.input_index,
number_of_samples=self.number_of_samples,
@@ -209,7 +209,7 @@ def nist_statistical_tests(self, test_type,
ratio = min(1, (number_of_blocks_in_one_sample - 1 - n) / math.comb(n, 2))
self.number_of_blocks_in_one_sample = int(1 + n + math.ceil(math.comb(n, 2) * ratio))
self.bits_in_one_sequence = self.number_of_blocks_in_one_sample * self.cipher.output_bit_size
- self._create_report_folder(statistical_test_option_list)
+ self._create_report_folder(time_date,statistical_test_option_list)
dataset = self.data_generator.generate_low_density_dataset(input_index=self.input_index,
number_of_samples=self.number_of_samples,
@@ -229,7 +229,7 @@ def nist_statistical_tests(self, test_type,
ratio = min(1, (number_of_blocks_in_one_sample - 1 - n) / math.comb(n, 2))
self.number_of_blocks_in_one_sample = int(1 + n + math.ceil(math.comb(n, 2) * ratio))
self.bits_in_one_sequence = self.number_of_blocks_in_one_sample * self.cipher.output_bit_size
- self._create_report_folder(statistical_test_option_list)
+ self._create_report_folder(time_date,statistical_test_option_list)
dataset = self.data_generator.generate_high_density_dataset(input_index=self.input_index,
number_of_samples=self.number_of_samples,
@@ -244,7 +244,7 @@ def nist_statistical_tests(self, test_type,
if not dataset:
return
self._write_execution_time(f'Compute {self.dataset_type.value}', dataset_generate_time)
- nist_test['test_results'] = self._generate_nist_dicts(dataset=dataset, round_start=round_start,
+ nist_test['test_results'] = self._generate_nist_dicts(time_date=time_date, dataset=dataset, round_start=round_start,
round_end=round_end,
statistical_test_option_list=statistical_test_option_list)
nist_test['input_parameters']['bits_in_one_sequence'] = bits_in_one_sequence
@@ -551,9 +551,9 @@ def _generate_chart_all(report_dict_list, report_folder="", show_graph=False):
plt.close()
print(f'Drawing chart for all rounds is in finished.')
- def _create_report_folder(self,statistical_test_option_list):
+ def _create_report_folder(self,time_date,statistical_test_option_list):
self.report_folder = os.path.join(self.folder_prefix,
- f'{self._cipher_primitive}_{self.dataset_type.name}_index{self.input_index}_{self.number_of_sequences}lines_{self.bits_in_one_sequence}bits_{statistical_test_option_list}test_option_list')
+ f'{self._cipher_primitive}_{self.dataset_type.name}_index{self.input_index}_{self.number_of_sequences}lines_{self.bits_in_one_sequence}bits_{statistical_test_option_list}test_option_list_{time_date}time')
try:
os.makedirs(self.report_folder)
except OSError:
@@ -567,7 +567,7 @@ def _write_execution_time(self, execution_description, execution_time):
except Exception as e:
print(f'Error: {e.strerror}')
- def _generate_nist_dicts(self, dataset, round_start, round_end, statistical_test_option_list='1' + 14 * '0'):
+ def _generate_nist_dicts(self,time_date, dataset, round_start, round_end, statistical_test_option_list='1' + 14 * '0'):
# seems that the statistical tools cannot change the default folder 'experiments'
nist_local_experiment_folder = f"/usr/local/bin/sts-2.1.2/experiments/"
dataset_folder = 'dataset'
@@ -591,7 +591,7 @@ def _generate_nist_dicts(self, dataset, round_start, round_end, statistical_test
print(f'Error: {e.strerror}')
return
- report_folder_round = os.path.abspath(os.path.join(self.report_folder, f'round_{round_number}'))
+ report_folder_round = os.path.abspath(os.path.join(self.report_folder, f'round_{round_number}_{time_date}time'))
dataset[round_number].tofile(dataset_filename)
sts_execution_time = time.time()
diff --git a/claasp/components/or_component.py b/claasp/components/or_component.py
index b4d961c2..7a962103 100644
--- a/claasp/components/or_component.py
+++ b/claasp/components/or_component.py
@@ -1,4 +1,3 @@
-
# ****************************************************************************
# Copyright 2023 Technology Innovation Institute
#
@@ -44,14 +43,43 @@ def algebraic_polynomials(self, model):
sage: or_component = gift.get_component_from_id("or_0_4")
sage: algebraic = AlgebraicModel(gift)
sage: or_component.algebraic_polynomials(algebraic)
- [or_0_4_y0 + 1,
- or_0_4_y1 + 1,
- ...
- or_0_4_y30 + 1,
- or_0_4_y31 + 1]
+ [or_0_4_x0*or_0_4_x32 + or_0_4_y0 + or_0_4_x32 + or_0_4_x0,
+ or_0_4_x1*or_0_4_x33 + or_0_4_y1 + or_0_4_x33 + or_0_4_x1,
+ or_0_4_x2*or_0_4_x34 + or_0_4_y2 + or_0_4_x34 + or_0_4_x2,
+ or_0_4_x3*or_0_4_x35 + or_0_4_y3 + or_0_4_x35 + or_0_4_x3,
+ or_0_4_x4*or_0_4_x36 + or_0_4_y4 + or_0_4_x36 + or_0_4_x4,
+ or_0_4_x5*or_0_4_x37 + or_0_4_y5 + or_0_4_x37 + or_0_4_x5,
+ or_0_4_x6*or_0_4_x38 + or_0_4_y6 + or_0_4_x38 + or_0_4_x6,
+ or_0_4_x7*or_0_4_x39 + or_0_4_y7 + or_0_4_x39 + or_0_4_x7,
+ or_0_4_x8*or_0_4_x40 + or_0_4_y8 + or_0_4_x40 + or_0_4_x8,
+ or_0_4_x9*or_0_4_x41 + or_0_4_y9 + or_0_4_x41 + or_0_4_x9,
+ or_0_4_x10*or_0_4_x42 + or_0_4_y10 + or_0_4_x42 + or_0_4_x10,
+ or_0_4_x11*or_0_4_x43 + or_0_4_y11 + or_0_4_x43 + or_0_4_x11,
+ or_0_4_x12*or_0_4_x44 + or_0_4_y12 + or_0_4_x44 + or_0_4_x12,
+ or_0_4_x13*or_0_4_x45 + or_0_4_y13 + or_0_4_x45 + or_0_4_x13,
+ or_0_4_x14*or_0_4_x46 + or_0_4_y14 + or_0_4_x46 + or_0_4_x14,
+ or_0_4_x15*or_0_4_x47 + or_0_4_y15 + or_0_4_x47 + or_0_4_x15,
+ or_0_4_x16*or_0_4_x48 + or_0_4_y16 + or_0_4_x48 + or_0_4_x16,
+ or_0_4_x17*or_0_4_x49 + or_0_4_y17 + or_0_4_x49 + or_0_4_x17,
+ or_0_4_x18*or_0_4_x50 + or_0_4_y18 + or_0_4_x50 + or_0_4_x18,
+ or_0_4_x19*or_0_4_x51 + or_0_4_y19 + or_0_4_x51 + or_0_4_x19,
+ or_0_4_x20*or_0_4_x52 + or_0_4_y20 + or_0_4_x52 + or_0_4_x20,
+ or_0_4_x21*or_0_4_x53 + or_0_4_y21 + or_0_4_x53 + or_0_4_x21,
+ or_0_4_x22*or_0_4_x54 + or_0_4_y22 + or_0_4_x54 + or_0_4_x22,
+ or_0_4_x23*or_0_4_x55 + or_0_4_y23 + or_0_4_x55 + or_0_4_x23,
+ or_0_4_x24*or_0_4_x56 + or_0_4_y24 + or_0_4_x56 + or_0_4_x24,
+ or_0_4_x25*or_0_4_x57 + or_0_4_y25 + or_0_4_x57 + or_0_4_x25,
+ or_0_4_x26*or_0_4_x58 + or_0_4_y26 + or_0_4_x58 + or_0_4_x26,
+ or_0_4_x27*or_0_4_x59 + or_0_4_y27 + or_0_4_x59 + or_0_4_x27,
+ or_0_4_x28*or_0_4_x60 + or_0_4_y28 + or_0_4_x60 + or_0_4_x28,
+ or_0_4_x29*or_0_4_x61 + or_0_4_y29 + or_0_4_x61 + or_0_4_x29,
+ or_0_4_x30*or_0_4_x62 + or_0_4_y30 + or_0_4_x62 + or_0_4_x30,
+ or_0_4_x31*or_0_4_x63 + or_0_4_y31 + or_0_4_x63 + or_0_4_x31]
+
"""
ninputs = self.input_bit_size
noutputs = self.output_bit_size
+ ors_number = self.description[1] - 1
word_size = noutputs
ring_R = model.ring()
input_vars = [self.id + "_" + model.input_postfix + str(i) for i in range(ninputs)]
@@ -61,10 +89,11 @@ def algebraic_polynomials(self, model):
def or_polynomial(x0, x1):
return x0 * x1 + x0 + x1
- x = [ring_R.one() for _ in range(noutputs)]
- for word_vars in words_vars:
+ x = [words_vars[0][_] for _ in range(noutputs)]
+ for or_itr in range(ors_number):
for i in range(noutputs):
- x[i] = or_polynomial(x[i], word_vars[i])
+ x[i] = or_polynomial(x[i], words_vars[or_itr + 1][i])
+
y = list(map(ring_R, output_vars))
polynomials = [y[i] + x[i] for i in range(noutputs)]
diff --git a/docker/Dockerfile b/docker/Dockerfile
index b35ad1bd..0658479a 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -303,6 +303,8 @@ RUN sage -pip install plotly -U kaleido
COPY required_dependencies/sage_numerical_backends_gurobi-9.3.1.tar.gz /opt/
RUN cd /opt/ && sage -pip install sage_numerical_backends_gurobi-9.3.1.tar.gz
+RUN apt-get install -y coinor-cbc coinor-libcbc-dev
+RUN sage -python -m pip install sage-numerical-backends-coin==9.0b12
WORKDIR /home/sage/tii-claasp
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 228b1d84..841c757c 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -5,6 +5,66 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2.3.0] - 2024-03-22
+
+### Added
+
+- Create new speedy cipher.
+- Create the a5/2 stream cipher.
+- Create a test function for nist statistical tests and dieharder statistical tests that produces a parsed result, standardized for the report class.
+- Gaston permutation with sbox component.
+- Create qarmav2 with mixcolumn component.
+- Support inversion of tweakable primitives and inversion of mixcolumn operation with a non irreducible polynomial.
+- Adding bct mzn model for arx ciphers.
+- Add option to start chacha permutation from a bottom lower round.
+- Adding minizinc boomerang model.
+
+### Changed
+
+- Location of files related to milp inequalities for non linear components or large xors moved to userspace.
+- Location of files related to milp external solvers change to current working directory.
+- Continuous_tests to class.
+
+### Fixed
+
+- Add timestamp to milp external files.
+- File path in cp module changed to absolute path.
+- Consider whole solution when searching xor linear trails.
+- Refactoring of algebraic tests to an object.
+- Create claasp base image for test.
+- Fix sonarcloud github action so forks can be analyzed on pr.
+
+## [2.2.0] - 2024-03-07
+
+### Added
+
+- Create new Speedy cipher
+- Create the A5/2 stream cipher
+- Create the grain128 stream cipher
+- Create a test function for nist statistical tests and dieharder statistical tests that produces a parsed result, standardized for the Report class
+- SAT wordwise deterministic truncated XOR differential trail model
+- Gaston permutation with sbox component
+- Create Qarmav2 with MixColumn component
+- Support inversion of tweakable primitives and inversion of MixColumn operation with a non irreducible polynomial
+- Add option to start Chacha permutation from a bottom lower round
+- Create bitwise impossible XOR differential trail search for SAT
+
+### Changed
+
+- Location of files related to MILP inequalities for non linear components or large xors moved to userspace
+- Location of files related to MILP external solvers change to current working directory
+- Continuous_tests to class
+
+### Fixed
+
+- Add timestamp to MILP external files
+- File path in CP module changed to absolute path
+- Consider whole solution when searching XOR linear trails
+- Refactored algebraic tests to an object and added some tests
+- Refactoring of algebraic tests to an object
+- Create CLAASP base image for test
+- Fix SonarCloud GitHub Action so Forks can be analyzed on PR
+
## [2.1.0] - 2024-01-30
### Added
@@ -89,6 +149,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Everything! First public release.
+[2.3.0]: https://github.com/Crypto-TII/claasp/compare/v2.3.0..v2.1.0
[2.1.0]: https://github.com/Crypto-TII/claasp/compare/v2.1.0..v2.0.0
[1.1.0]: https://github.com/Crypto-TII/claasp/releases/tag/v1.1.0
[1.0.0]: https://github.com/Crypto-TII/claasp/releases/tag/v1.0.0
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 2cc1bf73..cc4ef604 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -41,12 +41,13 @@ To contribute to this project, please, follow the following conventions.
# GitHub collaboration
-In order to collaborate with the project, you need to fork this projects.
+In order to collaborate with the project, you need to fill this [Google form](https://forms.gle/rYMKW76fCF15Lnxm6) to
+be added as a collaboratior in [CLAASP GitHub repository](https://github.com/Crypto-TII/claasp).
## Pull requests
-Pull requests are the way to contribute to the project. Pull requests coming from forks will be reviewed and need to
-have all the checks passing green.
+Pull requests are the way to contribute to the project. Only collaborators can create pull requests, so pull requests
+coming from forks will be rejected.
# Development environment
diff --git a/tests/unit/cipher_modules/models/algebraic/algebraic_model_test.py b/tests/unit/cipher_modules/models/algebraic/algebraic_model_test.py
index d6d8399e..42502e92 100644
--- a/tests/unit/cipher_modules/models/algebraic/algebraic_model_test.py
+++ b/tests/unit/cipher_modules/models/algebraic/algebraic_model_test.py
@@ -28,13 +28,13 @@ def test_nvars():
def test_polynomial_system():
fancy = FancyBlockCipher(number_of_rounds=1)
assert str(AlgebraicModel(fancy).polynomial_system()) == \
- 'Polynomial Sequence with 468 Polynomials in 384 Variables'
+ 'Polynomial Sequence with 228 Polynomials in 144 Variables'
def test_polynomial_system_at_round():
fancy = FancyBlockCipher(number_of_rounds=1)
assert str(AlgebraicModel(fancy).polynomial_system_at_round(0)) == \
- 'Polynomial Sequence with 252 Polynomials in 288 Variables'
+ 'Polynomial Sequence with 228 Polynomials in 144 Variables'
def test_ring():
diff --git a/tests/unit/cipher_modules/models/milp/milp_models/milp_bitwise_impossible_xor_differential_model_test.py b/tests/unit/cipher_modules/models/milp/milp_models/milp_bitwise_impossible_xor_differential_model_test.py
index 27d35a78..8d205674 100644
--- a/tests/unit/cipher_modules/models/milp/milp_models/milp_bitwise_impossible_xor_differential_model_test.py
+++ b/tests/unit/cipher_modules/models/milp/milp_models/milp_bitwise_impossible_xor_differential_model_test.py
@@ -128,7 +128,7 @@ def test_find_one_bitwise_impossible_xor_differential_trail_model_with_external_
key = set_fixed_variables(component_id='key', constraint_type='equal', bit_positions=range(64), bit_values=[0] * 64)
ciphertext = set_fixed_variables(component_id='cipher_output_10_13', constraint_type='equal',
bit_positions=range(32), bit_values=[0] * 6 + [2, 0, 2] + [0] * 23)
- trail = milp.find_one_bitwise_impossible_xor_differential_trail(6, fixed_values=[plaintext, key, ciphertext], external_solver_name='glpk')
+ trail = milp.find_one_bitwise_impossible_xor_differential_trail(6, fixed_values=[plaintext, key, ciphertext], external_solver_name='glpk_ext')
assert trail['status'] == 'SATISFIABLE'
assert trail['components_values']['intermediate_output_5_12']['value'] == '????????????????0??????1??????0?'
assert trail['components_values']['intermediate_output_5_12_backward']['value'] == SIMON_INCOMPATIBLE_ROUND_OUTPUT
@@ -145,7 +145,7 @@ def test_find_one_bitwise_impossible_xor_differential_trail_with_fully_automatic
ciphertext_backward = set_fixed_variables(component_id='cipher_output_10_13_backward', constraint_type='equal',
bit_positions=range(32), bit_values=[0] * 6 + [2, 0, 2] + [0] * 23)
trail = milp.find_one_bitwise_impossible_xor_differential_trail_with_fully_automatic_model(
- fixed_values=[plaintext, key, key_backward, ciphertext_backward], external_solver_name='glpk')
+ fixed_values=[plaintext, key, key_backward, ciphertext_backward], external_solver_name='glpk_ext')
assert trail['status'] == 'SATISFIABLE'
assert trail['components_values']['plaintext']['value'] == '00000000000000000000000000000001'
assert trail['components_values']['intermediate_output_5_12_backward']['value'] == SIMON_INCOMPATIBLE_ROUND_OUTPUT
diff --git a/tests/unit/cipher_modules/models/milp/milp_models/milp_wordwise_impossible_xor_differential_model_test.py b/tests/unit/cipher_modules/models/milp/milp_models/milp_wordwise_impossible_xor_differential_model_test.py
index 8ba9cce4..3f0b8cd8 100644
--- a/tests/unit/cipher_modules/models/milp/milp_models/milp_wordwise_impossible_xor_differential_model_test.py
+++ b/tests/unit/cipher_modules/models/milp/milp_models/milp_wordwise_impossible_xor_differential_model_test.py
@@ -93,7 +93,7 @@ def test_find_one_wordwise_impossible_xor_differential_trail_model_with_external
ciphertext = set_fixed_variables(component_id='cipher_output_1_32', constraint_type='equal', bit_positions=range(16),
bit_values=[1] + [0]*15)
trail = milp.find_one_wordwise_impossible_xor_differential_trail(1, fixed_bits=[key],
- fixed_words=[plaintext, ciphertext], external_solver_name='glpk')
+ fixed_words=[plaintext, ciphertext], external_solver_name='glpk_ext')
assert trail['status'] == 'SATISFIABLE'
assert trail['components_values']['plaintext']['value'] == '1003000000000000'
assert trail['components_values']['key']['value'] == '0000000000000000'
@@ -113,7 +113,7 @@ def test_find_one_wordwise_impossible_xor_differential_trail_with_fully_automati
ciphertext_backward = set_fixed_variables(component_id='cipher_output_1_32_backward', constraint_type='equal', bit_positions=range(16),
bit_values=[1] + [0]*15)
trail = milp.find_one_wordwise_impossible_xor_differential_trail_with_fully_automatic_model(fixed_bits=[key, key_backward],
- fixed_words=[plaintext, ciphertext_backward], external_solver_name='glpk')
+ fixed_words=[plaintext, ciphertext_backward], external_solver_name='glpk_ext')
assert trail['status'] == 'SATISFIABLE'
assert trail['components_values']['plaintext']['value'] == '1003000000000000'
assert trail['components_values']['key']['value'] == '0000000000000000'
diff --git a/tests/unit/cipher_modules/models/milp/milp_models/milp_xor_linear_model_test.py b/tests/unit/cipher_modules/models/milp/milp_models/milp_xor_linear_model_test.py
index 9b4875cb..a37799b7 100644
--- a/tests/unit/cipher_modules/models/milp/milp_models/milp_xor_linear_model_test.py
+++ b/tests/unit/cipher_modules/models/milp/milp_models/milp_xor_linear_model_test.py
@@ -114,7 +114,7 @@ def test_find_one_xor_linear_trail_with_fixed_weight():
def test_find_one_xor_linear_trail_with_fixed_weight_with_external_solver():
speck = SpeckBlockCipher(block_bit_size=32, key_bit_size=64, number_of_rounds=2)
milp = MilpXorLinearModel(speck)
- trail = milp.find_one_xor_linear_trail_with_fixed_weight(1, external_solver_name="glpk")
+ trail = milp.find_one_xor_linear_trail_with_fixed_weight(1, external_solver_name="glpk_ext")
assert len(trail) == 10
assert trail["total_weight"] == 1.0
@@ -123,14 +123,14 @@ def test_find_one_xor_linear_trail_with_fixed_weight_with_supported_but_not_inst
with pytest.raises(Exception) as e_info:
speck = SpeckBlockCipher(block_bit_size=32, key_bit_size=64, number_of_rounds=2)
milp = MilpXorLinearModel(speck)
- trail = milp.find_one_xor_linear_trail_with_fixed_weight(1, external_solver_name="cplex")
+ trail = milp.find_one_xor_linear_trail_with_fixed_weight(1, external_solver_name="cplex_ext")
def test_find_one_xor_linear_trail_with_fixed_weight_with_installed_external_solver_but_missing_license():
with pytest.raises(Exception) as e_info:
speck = SpeckBlockCipher(block_bit_size=32, key_bit_size=64, number_of_rounds=2)
milp = MilpXorLinearModel(speck)
- trail = milp.find_one_xor_linear_trail_with_fixed_weight(1, external_solver_name="Gurobi")
+ trail = milp.find_one_xor_linear_trail_with_fixed_weight(1, external_solver_name="Gurobi_ext")
def test_find_one_xor_linear_trail_with_fixed_weight_with_unsupported_external_solver():
with pytest.raises(Exception) as e_info:
diff --git a/tests/unit/cipher_modules/report_test.py b/tests/unit/cipher_modules/report_test.py
index f2bb9060..18c1b067 100644
--- a/tests/unit/cipher_modules/report_test.py
+++ b/tests/unit/cipher_modules/report_test.py
@@ -6,6 +6,7 @@
from claasp.ciphers.block_ciphers.speck_block_cipher import SpeckBlockCipher
from claasp.ciphers.block_ciphers.simon_block_cipher import SimonBlockCipher
from claasp.cipher_modules.report import Report
+from claasp.ciphers.block_ciphers.present_block_cipher import PresentBlockCipher
from claasp.cipher_modules.statistical_tests.dieharder_statistical_tests import DieharderTests
from claasp.cipher_modules.statistical_tests.nist_statistical_tests import NISTStatisticalTests
from claasp.cipher_modules.neural_network_tests import NeuralNetworkTests
@@ -74,8 +75,8 @@ def test_save_as_latex_table():
trail_report = Report(trail)
trail_report.save_as_latex_table()
- nist = NISTStatisticalTests(simon)
- report_sts = Report(nist.nist_statistical_tests('avalanche'))
+ dieharder=DieharderTests(simon)
+ report_sts = Report(dieharder.dieharder_statistical_tests('avalanche', dieharder_test_option=100))
report_sts.save_as_latex_table()
def test_save_as_DataFrame():
@@ -100,8 +101,8 @@ def test_save_as_DataFrame():
trail_report = Report(trail)
trail_report.save_as_DataFrame()
- nist = NISTStatisticalTests(speck)
- report_sts = Report(nist.nist_statistical_tests('avalanche'))
+ dieharder = DieharderTests(speck)
+ report_sts = Report(dieharder.dieharder_statistical_tests('avalanche', dieharder_test_option=100))
report_sts.save_as_DataFrame()
@@ -112,24 +113,23 @@ def test_save_as_json():
simon).neural_network_blackbox_distinguisher_tests()
blackbox_report = Report(neural_network_blackbox_distinguisher_tests_results)
blackbox_report.save_as_json(fixed_input='plaintext',fixed_output='round_output')
- nist = NISTStatisticalTests(simon)
- report_sts = Report(nist.nist_statistical_tests('avalanche'))
+ dieharder = DieharderTests(simon)
+ report_sts = Report(dieharder.dieharder_statistical_tests('avalanche', dieharder_test_option=100))
report_sts.save_as_json()
- milp = MilpXorDifferentialModel(simon)
- 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_lowest_weight_xor_differential_trail(fixed_values=[plaintext, key])
+ present = PresentBlockCipher(number_of_rounds=2)
+ sat = SatXorDifferentialModel(present)
+ related_key_setting = [
+ set_fixed_variables(component_id='key', constraint_type='not_equal', bit_positions=list(range(80)),
+ bit_values=[0] * 80),
+ set_fixed_variables(component_id='plaintext', constraint_type='equal', bit_positions=list(range(64)),
+ bit_values=[0] * 64)
+ ]
+ trail = sat.find_one_xor_differential_trail_with_fixed_weight(fixed_weight=16, fixed_values=related_key_setting,
+ solver_name='kissat')
trail_report = Report(trail)
- trail_report.save_as_json()
+ trail_report.show()
+
avalanche_results = AvalancheTests(simon).avalanche_tests()
avalanche_report = Report(avalanche_results)
avalanche_report.save_as_json(fixed_input='plaintext',fixed_output='round_output',fixed_test='avalanche_weight_vectors')
@@ -157,24 +157,18 @@ def test_show():
avalanche_report.show(test_name='avalanche_weight_vectors', fixed_input_difference=None)
avalanche_report.show(test_name='avalanche_weight_vectors', fixed_input_difference='average')
- 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])
+ present = PresentBlockCipher(number_of_rounds=4)
+ sat = SatXorDifferentialModel(present)
+ related_key_setting = [
+ set_fixed_variables(component_id='key', constraint_type='not_equal', bit_positions=list(range(80)),
+ bit_values=[0] * 80)]
+ trail = sat.find_one_xor_differential_trail_with_fixed_weight(fixed_weight=16, fixed_values=related_key_setting,
+ solver_name='kissat')
trail_report = Report(trail)
trail_report.show()
- nist = NISTStatisticalTests(speck)
- report_sts = Report(nist.nist_statistical_tests('avalanche'))
+ dieharder = DieharderTests(speck)
+ report_sts = Report(dieharder.dieharder_statistical_tests('avalanche', dieharder_test_option=100))
report_sts.show()
neural_network_tests = NeuralNetworkTests(speck).neural_network_differential_distinguisher_tests()
diff --git a/tests/unit/cipher_test.py b/tests/unit/cipher_test.py
index 06d03617..5a053ea1 100644
--- a/tests/unit/cipher_test.py
+++ b/tests/unit/cipher_test.py
@@ -53,33 +53,33 @@ def test_algebraic_tests():
d = AlgebraicTests(toyspn).algebraic_tests(10)
assert d == {
'input_parameters': {'cipher': toyspn, 'timeout_in_seconds': 10, 'test_name': 'algebraic_tests'},
- 'test_results': {'number_of_variables': [66, 126],
- 'number_of_equations': [76, 158],
- 'number_of_monomials': [96, 186],
+ 'test_results': {'number_of_variables': [30, 48],
+ 'number_of_equations': [40, 80],
+ 'number_of_monomials': [60, 108],
'max_degree_of_equations': [2, 2],
- 'test_passed': [False, True]}}
+ 'test_passed': [False, False]}}
speck = SpeckBlockCipher(block_bit_size=32, key_bit_size=64, number_of_rounds=1)
d = AlgebraicTests(speck).algebraic_tests(1)
assert d == {'input_parameters': {'cipher': speck,
- 'timeout_in_seconds': 1,
- 'test_name': 'algebraic_tests'},
- 'test_results': {'number_of_variables': [320],
- 'number_of_equations': [272],
- 'number_of_monomials': [365],
+ 'timeout_in_seconds': 1,
+ 'test_name': 'algebraic_tests'},
+ 'test_results': {'number_of_variables': [144],
+ 'number_of_equations': [96],
+ 'number_of_monomials': [189],
'max_degree_of_equations': [2],
'test_passed': [True]}}
aes = AESBlockCipher(word_size=4, state_size=2, number_of_rounds=1)
d = AlgebraicTests(aes).algebraic_tests(5)
compare_result = {'input_parameters': {'cipher': aes,
- 'timeout_in_seconds': 5,
- 'test_name': 'algebraic_tests'},
- 'test_results': {'number_of_variables': [320],
- 'number_of_equations': [390],
- 'number_of_monomials': [488],
- 'max_degree_of_equations': [2],
- 'test_passed': [False]}}
+ 'timeout_in_seconds': 5,
+ 'test_name': 'algebraic_tests'},
+ 'test_results': {'number_of_variables': [128],
+ 'number_of_equations': [198],
+ 'number_of_monomials': [296],
+ 'max_degree_of_equations': [2],
+ 'test_passed': [False]}}
assert d == compare_result
@@ -202,8 +202,8 @@ def test_impossible_differential_search():
def test_is_algebraically_secure():
- identity = IdentityBlockCipher()
- assert identity.is_algebraically_secure(120) is False
+ aes = AESBlockCipher(word_size=4, state_size=2, number_of_rounds = 1)
+ assert aes.is_algebraically_secure(20) is False
def test_is_andrx():
@@ -233,12 +233,13 @@ def test_is_spn():
def test_polynomial_system():
- assert str(IdentityBlockCipher().polynomial_system()) == 'Polynomial Sequence with 128 Polynomials in 256 Variables'
+ tea = TeaBlockCipher(block_bit_size=32, key_bit_size=64, number_of_rounds=1)
+ assert str(tea.polynomial_system()) == 'Polynomial Sequence with 352 Polynomials in 448 Variables'
def test_polynomial_system_at_round():
assert str(FancyBlockCipher(number_of_rounds=1).polynomial_system_at_round(0)) == \
- 'Polynomial Sequence with 252 Polynomials in 288 Variables'
+ 'Polynomial Sequence with 228 Polynomials in 144 Variables'
def test_print():
diff --git a/tests/unit/components/or_component_test.py b/tests/unit/components/or_component_test.py
index d10d56b1..204a3530 100644
--- a/tests/unit/components/or_component_test.py
+++ b/tests/unit/components/or_component_test.py
@@ -10,10 +10,10 @@ def test_algebraic_polynomials():
algebraic = AlgebraicModel(gift)
algebraic_polynomials = or_component.algebraic_polynomials(algebraic)
- assert str(algebraic_polynomials[0]) == "or_0_4_y0 + 1"
- assert str(algebraic_polynomials[1]) == "or_0_4_y1 + 1"
- assert str(algebraic_polynomials[-2]) == "or_0_4_y30 + 1"
- assert str(algebraic_polynomials[-1]) == "or_0_4_y31 + 1"
+ assert str(algebraic_polynomials[0]) == "or_0_4_x0*or_0_4_x32 + or_0_4_y0 + or_0_4_x32 + or_0_4_x0"
+ assert str(algebraic_polynomials[1]) == "or_0_4_x1*or_0_4_x33 + or_0_4_y1 + or_0_4_x33 + or_0_4_x1"
+ assert str(algebraic_polynomials[-2]) == "or_0_4_x30*or_0_4_x62 + or_0_4_y30 + or_0_4_x62 + or_0_4_x30"
+ assert str(algebraic_polynomials[-1]) == "or_0_4_x31*or_0_4_x63 + or_0_4_y31 + or_0_4_x63 + or_0_4_x31"
def test_cp_constraints():