Skip to content

Commit

Permalink
Merge pull request #252 from Crypto-TII/feat/cp-undisturbed-bits-cons…
Browse files Browse the repository at this point in the history
…traints

Feat/cp undisturbed bits constraints
  • Loading branch information
peacker authored Jul 9, 2024
2 parents 8e4fb51 + 74f2e93 commit 5820774
Show file tree
Hide file tree
Showing 26 changed files with 1,382 additions and 756 deletions.
1 change: 1 addition & 0 deletions claasp/cipher_modules/models/cp/cp_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ def get_command_for_solver_process(self, input_file_path, model_type, solver_nam
'xor_linear_one_solution',
'deterministic_truncated_xor_differential_one_solution',
'impossible_xor_differential_one_solution',
'impossible_xor_differential_attack',
'differential_pair_one_solution',
'evaluate_cipher']
write_model_to_file(self._model_constraints, input_file_path)
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@

# ****************************************************************************
# 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 <https://www.gnu.org/licenses/>.
# ****************************************************************************


import os
import math
import itertools
import subprocess

from claasp.cipher_modules.models.cp.cp_models.cp_deterministic_truncated_xor_differential_model import CpDeterministicTruncatedXorDifferentialModel, solve_satisfy
from claasp.cipher_modules.models.utils import write_model_to_file, convert_solver_solution_to_dictionary
from claasp.name_mappings import (CONSTANT, INTERMEDIATE_OUTPUT, CIPHER_OUTPUT, LINEAR_LAYER, SBOX, MIX_COLUMN,
WORD_OPERATION, DETERMINISTIC_TRUNCATED_XOR_DIFFERENTIAL)
from claasp.cipher_modules.models.cp.solvers import MODEL_DEFAULT_PATH, SOLVER_DEFAULT


class CpWordwiseDeterministicTruncatedXorDifferentialModel(CpDeterministicTruncatedXorDifferentialModel):

def __init__(self, cipher):
super().__init__(cipher)

def final_wordwise_deterministic_truncated_xor_differential_constraints(self, minimize=False):
"""
Return a CP constraints list for the cipher outputs and solving indications for wordwise model.
INPUT:
- None
EXAMPLES::
sage: from claasp.ciphers.block_ciphers.speck_block_cipher import SpeckBlockCipher
sage: from claasp.cipher_modules.models.cp.cp_models.cp_wordwise_deterministic_truncated_xor_differential_model import CpWordwiseDeterministicTruncatedXorDifferentialModel
sage: speck = SpeckBlockCipher(number_of_rounds=4)
sage: cp = CpWordwiseDeterministicTruncatedXorDifferentialModel(speck)
sage: cp.final_wordwise_deterministic_truncated_xor_differential_constraints()
['solve satisfy;',
'output["plaintext_active = "++ show(plaintext_active) ++ "\\n" ++"key_active = "++ show(key_active) ++ "\\n" ++"rot_0_0 = "++ show(rot_0_0_active)++ "\\n" ++ "0" ++ "\\n" ++"modadd_0_1 = "++ show(modadd_0_1_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_0_2 = "++ show(xor_0_2_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_0_3 = "++ show(rot_0_3_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_0_4 = "++ show(xor_0_4_active)++ "\\n" ++ "0" ++ "\\n" ++"intermediate_output_0_5 = "++ show(intermediate_output_0_5_active)++ "\\n" ++ "0" ++ "\\n" ++"intermediate_output_0_6 = "++ show(intermediate_output_0_6_active)++ "\\n" ++ "0" ++ "\\n" ++"constant_1_0 = "++ show(constant_1_0_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_1_1 = "++ show(rot_1_1_active)++ "\\n" ++ "0" ++ "\\n" ++"modadd_1_2 = "++ show(modadd_1_2_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_1_3 = "++ show(xor_1_3_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_1_4 = "++ show(rot_1_4_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_1_5 = "++ show(xor_1_5_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_1_6 = "++ show(rot_1_6_active)++ "\\n" ++ "0" ++ "\\n" ++"modadd_1_7 = "++ show(modadd_1_7_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_1_8 = "++ show(xor_1_8_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_1_9 = "++ show(rot_1_9_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_1_10 = "++ show(xor_1_10_active)++ "\\n" ++ "0" ++ "\\n" ++"intermediate_output_1_11 = "++ show(intermediate_output_1_11_active)++ "\\n" ++ "0" ++ "\\n" ++"intermediate_output_1_12 = "++ show(intermediate_output_1_12_active)++ "\\n" ++ "0" ++ "\\n" ++"constant_2_0 = "++ show(constant_2_0_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_2_1 = "++ show(rot_2_1_active)++ "\\n" ++ "0" ++ "\\n" ++"modadd_2_2 = "++ show(modadd_2_2_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_2_3 = "++ show(xor_2_3_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_2_4 = "++ show(rot_2_4_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_2_5 = "++ show(xor_2_5_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_2_6 = "++ show(rot_2_6_active)++ "\\n" ++ "0" ++ "\\n" ++"modadd_2_7 = "++ show(modadd_2_7_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_2_8 = "++ show(xor_2_8_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_2_9 = "++ show(rot_2_9_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_2_10 = "++ show(xor_2_10_active)++ "\\n" ++ "0" ++ "\\n" ++"intermediate_output_2_11 = "++ show(intermediate_output_2_11_active)++ "\\n" ++ "0" ++ "\\n" ++"intermediate_output_2_12 = "++ show(intermediate_output_2_12_active)++ "\\n" ++ "0" ++ "\\n" ++"constant_3_0 = "++ show(constant_3_0_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_3_1 = "++ show(rot_3_1_active)++ "\\n" ++ "0" ++ "\\n" ++"modadd_3_2 = "++ show(modadd_3_2_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_3_3 = "++ show(xor_3_3_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_3_4 = "++ show(rot_3_4_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_3_5 = "++ show(xor_3_5_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_3_6 = "++ show(rot_3_6_active)++ "\\n" ++ "0" ++ "\\n" ++"modadd_3_7 = "++ show(modadd_3_7_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_3_8 = "++ show(xor_3_8_active)++ "\\n" ++ "0" ++ "\\n" ++"rot_3_9 = "++ show(rot_3_9_active)++ "\\n" ++ "0" ++ "\\n" ++"xor_3_10 = "++ show(xor_3_10_active)++ "\\n" ++ "0" ++ "\\n" ++"intermediate_output_3_11 = "++ show(intermediate_output_3_11_active)++ "\\n" ++ "0" ++ "\\n" ++"cipher_output_3_12 = "++ show(cipher_output_3_12_active)++ "\\n" ++ "0" ++ "\\n" ];']
"""
cipher_inputs = self._cipher.inputs
cipher = self._cipher
cp_constraints = []
new_constraint = 'output['
for element in cipher_inputs:
new_constraint = f'{new_constraint}\"{element}_active = \"++ show({element}_active) ++ \"\\n\" ++'
for component_id in cipher.get_all_components_ids():
new_constraint = new_constraint + \
f'\"{component_id} = \"++ show({component_id}_active)++ \"\\n\" ++ \"0\" ++ \"\\n\" ++'
if 'cipher_output' in component_id and minimize:
cp_constraints.append(f'solve maximize count({self._cipher.get_all_components_ids()[-1]}_active, 0);')
new_constraint = new_constraint[:-2] + '];'
if cp_constraints == []:
cp_constraints.append(solve_satisfy)
cp_constraints.append(new_constraint)

return cp_constraints

def find_one_wordwise_deterministic_truncated_xor_differential_trail(self, number_of_rounds=None,
fixed_values=[], solver_name=SOLVER_DEFAULT, num_of_processors=None, timelimit=None):

if number_of_rounds is None:
number_of_rounds = self._cipher.number_of_rounds

self.build_deterministic_truncated_xor_differential_trail_model(fixed_values, number_of_rounds, wordwise=True)

return self.solve('deterministic_truncated_xor_differential_one_solution', solver_name, num_of_processors, timelimit)

def input_wordwise_deterministic_truncated_xor_differential_constraints(self):

cp_constraints = []
cp_declarations = []
for input_, bit_size in zip(self._cipher.inputs, self._cipher.inputs_bit_size):
cp_declarations.append(f'array[0..{bit_size // self.word_size - 1}] of var 0..3: {input_}_active;')
cp_declarations.append(
f'array[0..{bit_size // self.word_size - 1}] of var -2..{2 ** self.word_size - 1}: {input_}_value;')
for i in range(bit_size // self.word_size):
cp_constraints.append(f'constraint if {input_}_active[{i}] == 0 then {input_}_value[{i}] = 0 elseif '
f'{input_}_active[{i}] == 1 then {input_}_value[{i}] > 0 elseif '
f'{input_}_active[{i}] == 2 then {input_}_value[{i}] =-1 else '
f'{input_}_value[{i}] =-2 endif;')
for component in self._cipher.get_all_components():
if CONSTANT not in component.type:
output_id_link = component.id
output_size = int(component.output_bit_size)
cp_declarations.append(
f'array[0..{output_size // self.word_size - 1}] of var 0..3: {output_id_link}_active;')
cp_declarations.append(
f'array[0..{output_size // self.word_size - 1}] of var -2..{2 ** self.word_size - 1}: '
f'{output_id_link}_value;')
for i in range(output_size // self.word_size):
cp_constraints.append(
f'constraint if {output_id_link}_active[{i}] == 0 then {output_id_link}_value[{i}] = 0 elseif '
f'{output_id_link}_active[{i}] == 1 then {output_id_link}_value[{i}] > 0 elseif '
f'{output_id_link}_active[{i}] == 2 then {output_id_link}_value[{i}] =-1 else '
f'{output_id_link}_value[{i}] =-2 endif;')
if CIPHER_OUTPUT in component.type:
cp_constraints.append(f'constraint count({output_id_link}_active,2) < {output_size};')
cp_constraints.append('constraint count(plaintext_active,1) > 0;')

return cp_declarations, cp_constraints

Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ def find_differential_weight(self, fixed_values=[], solver_name=SOLVER_DEFAULT):
else:
return solutions['total_weight']

def find_lowest_weight_xor_differential_trail(self, fixed_values=[], solver_name='Chuffed'):
def find_lowest_weight_xor_differential_trail(self, fixed_values=[], solver_name=SOLVER_DEFAULT, num_of_processors=None, timelimit=None):
"""
Return the solution representing a differential trail with the lowest probability weight.
By default, the search is set in the single-key setting.
Expand Down Expand Up @@ -367,12 +367,12 @@ def find_lowest_weight_xor_differential_trail(self, fixed_values=[], solver_name
self.build_xor_differential_trail_model(-1, fixed_values)
end = tm.time()
build_time = end - start
solution = self.solve('xor_differential_one_solution', solver_name)
solution = self.solve('xor_differential_one_solution', solver_name, num_of_processors, timelimit)
solution['building_time_seconds'] = build_time
solution['test_name'] = "find_lowest_weight_xor_differential_trail"
return solution

def find_one_xor_differential_trail(self, fixed_values=[], solver_name=SOLVER_DEFAULT):
def find_one_xor_differential_trail(self, fixed_values=[], solver_name=SOLVER_DEFAULT, num_of_processors=None, timelimit=None):
"""
Return the solution representing a differential trail with any weight.
By default, the search is set in the single-key setting.
Expand Down Expand Up @@ -420,7 +420,7 @@ def find_one_xor_differential_trail(self, fixed_values=[], solver_name=SOLVER_DE
return solution

def find_one_xor_differential_trail_with_fixed_weight(self, fixed_weight=-1, fixed_values=[],
solver_name=SOLVER_DEFAULT):
solver_name=SOLVER_DEFAULT, num_of_processors=None, timelimit=None):
"""
Return the solution representing a differential trail with the weight of probability equal to ``fixed_weight``.
By default, the search is set in the single-key setting.
Expand Down
Loading

0 comments on commit 5820774

Please sign in to comment.