Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix/eliminate rotate shift not components polynomials #219

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions claasp/cipher_modules/algebraic_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ class AlgebraicTests:
{'input_parameters': {'cipher': toyspn1_p6_k6_o6_r2,
'timeout_in_seconds': 10,
'test_name': 'algebraic_tests'},
'test_results': {'number_of_variables': [30, 48],
'number_of_equations': [40, 80],
'number_of_monomials': [60, 108],
'test_results': {'number_of_variables': [24, 42],
'number_of_equations': [34, 74],
'number_of_monomials': [54, 102],
'max_degree_of_equations': [2, 2],
'test_passed': [False, False]}}

Expand All @@ -48,9 +48,9 @@ class AlgebraicTests:
{'input_parameters': {'cipher': speck_p32_k64_o32_r1,
'timeout_in_seconds': 30,
'test_name': 'algebraic_tests'},
'test_results': {'number_of_variables': [144],
'number_of_equations': [96],
'number_of_monomials': [189],
'test_results': {'number_of_variables': [112],
'number_of_equations': [64],
'number_of_monomials': [157],
'max_degree_of_equations': [2],
'test_passed': [True]}}

Expand All @@ -69,12 +69,15 @@ def algebraic_tests(self, timeout_in_seconds=60):
tests_up_to_round = []

F = []
constant_vars = {}
dict_vars = {}
for round_number in range(self._cipher.number_of_rounds):
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)
dict_vars.update(self._algebraic_model._dict_const_rot_not_shift_component_polynomials(round_number))
if round_number == self._cipher.number_of_rounds - 1 and dict_vars:
dict_vars = self._algebraic_model._substitute_cipher_output_vars_dict_vars(dict_vars, round_number)
if dict_vars:
F = self._algebraic_model._eliminate_const_not_shift_rot_components_polynomials(dict_vars, F)

Fseq = Sequence(F)
nvars_up_to_round.append(Fseq.nvariables())
npolynomials_up_to_round.append(len(Fseq))
Expand Down
112 changes: 77 additions & 35 deletions claasp/cipher_modules/models/algebraic/algebraic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def polynomial_system(self):
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
Polynomial Sequence with 74 Polynomials in 42 Variables

sage: from claasp.ciphers.block_ciphers.fancy_block_cipher import FancyBlockCipher
sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
Expand All @@ -165,32 +165,55 @@ def polynomial_system(self):
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
Polynomial Sequence with 192 Polynomials in 256 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
Polynomial Sequence with 174 Polynomials in 104 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
Polynomial Sequence with 288 Polynomials in 384 Variables

sage: from claasp.ciphers.permutations.gift_permutation import GiftPermutation
sage: from claasp.cipher_modules.models.algebraic.algebraic_model import AlgebraicModel
sage: gift = GiftPermutation(number_of_rounds=1)
sage: AlgebraicModel(gift).polynomial_system()
Polynomial Sequence with 448 Polynomials in 640 Variables


"""
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)
dict_vars = {}

for round_number in range(self._cipher.number_of_rounds):
polynomials += self.polynomial_system_at_round(round_number, True)

dict_vars.update(self._dict_const_rot_not_shift_component_polynomials(round_number))
if round_number == self._cipher.number_of_rounds - 1 and dict_vars:
dict_vars = self._substitute_cipher_output_vars_dict_vars(dict_vars, round_number)
if dict_vars:
polynomials = self._eliminate_const_not_shift_rot_components_polynomials(dict_vars, polynomials)
return Sequence(polynomials)

def polynomial_system_at_round(self, r, fun_call_flag=False):
def _substitute_cipher_output_vars_dict_vars(self, dict_vars, round_number):
cipher_dict = {}
cipher_component = self._cipher.get_components_in_round(round_number)[-1]
input_vars, prev_input_vars = self._input_vars_previous_input_vars(cipher_component)
cipher_dict.update({y: x for x, y in zip(input_vars, prev_input_vars)})
sub_dict_vars = {}
for k, val in dict_vars.items():
if val not in {0, 1}:
sub_dict_vars[k] = val.subs(cipher_dict)
else:
sub_dict_vars[k] = val
return sub_dict_vars

def polynomial_system_at_round(self, r, method_call_flag=False):
"""
Return a polynomial system at round `r`.

Expand Down Expand Up @@ -226,11 +249,12 @@ def polynomial_system_at_round(self, r, fun_call_flag=False):

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)

if method_call_flag is False:
dict_vars = self._dict_const_rot_not_shift_component_polynomials(r)
if r == self._cipher.number_of_rounds - 1 and dict_vars:
dict_vars = self._substitute_cipher_output_vars_dict_vars(dict_vars, r)
if dict_vars:
polynomials = self._eliminate_const_not_shift_rot_components_polynomials(dict_vars, polynomials)
return Sequence(polynomials)

def _apply_connection_variable_mapping(self, polys, r):
Expand All @@ -239,7 +263,6 @@ def _apply_connection_variable_mapping(self, polys, r):
return polys

variable_substitution_dict = {}

for component in self._cipher.get_components_in_round(r):
if component.type == "constant":
continue
Expand All @@ -248,7 +271,6 @@ def _apply_connection_variable_mapping(self, polys, r):
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
Expand All @@ -266,25 +288,45 @@ def _input_vars_previous_input_vars(self, component):
prev_input_vars = list(map(self.ring(), prev_input_vars))
return input_vars, prev_input_vars

def _dict_constant_component_polynomials(self, round_number):
def _dict_const_rot_not_shift_component_polynomials(self, round_number):

constant_vars = {}
dict_vars = {}
word_operation = ["ROTATE", "SHIFT", "NOT"]
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)
if component.type == "constant" or (
component.type == "word_operation" and component.description[0] in word_operation):
x = [component.id + "_" + self.output_postfix + str(i) for i in
range(component.output_bit_size)]

x = list(map(self.ring(), x))
input_links = component.input_id_links
input_positions = component.input_bit_positions
y = []
for k in range(len(input_links)):
y += [input_links[k] + "_" + self.output_postfix + str(i) for i in
input_positions[k]]
y = list(map(self.ring(), y))
noutputs = component.output_bit_size
if component.type == "constant":
constant = int(component.description[0], 16)
b = list(map(int, reversed(bin(constant)[2:])))
b += [0] * (noutputs - len(b))
dict_vars.update({x: y for x, y in zip(x, b)})
else:
if component.description[0] == 'ROTATE':
rotation_const = component.description[1]
dict_vars.update({x[i]: y[(rotation_const + i) % noutputs] for i in range(len(x))})
elif component.description[0] == 'SHIFT':
shift_constant = component.description[1] % noutputs
dict_vars.update({x[i]: 0 for i in range(shift_constant)})
dict_vars.update({x[shift_constant:][i]: y[i] for i in range(noutputs - shift_constant)})
else:
dict_vars.update({x[i]: y[i] + 1 for i in range(len(x))})

return dict_vars

def _eliminate_const_not_shift_rot_components_polynomials(self, dict_vars, polys):
polys = Sequence(polys).subs(dict_vars)
polys = [p for p in polys if p != 0]
return polys

Expand Down
20 changes: 10 additions & 10 deletions tests/unit/cipher_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ 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': [30, 48],
'number_of_equations': [40, 80],
'number_of_monomials': [60, 108],
'test_results': {'number_of_variables': [24, 42],
'number_of_equations': [34, 74],
'number_of_monomials': [54, 102],
'max_degree_of_equations': [2, 2],
'test_passed': [False, False]}}

Expand All @@ -64,9 +64,9 @@ def test_algebraic_tests():
assert d == {'input_parameters': {'cipher': speck,
'timeout_in_seconds': 1,
'test_name': 'algebraic_tests'},
'test_results': {'number_of_variables': [144],
'number_of_equations': [96],
'number_of_monomials': [189],
'test_results': {'number_of_variables': [112],
'number_of_equations': [64],
'number_of_monomials': [157],
'max_degree_of_equations': [2],
'test_passed': [True]}}

Expand All @@ -75,9 +75,9 @@ def test_algebraic_tests():
compare_result = {'input_parameters': {'cipher': aes,
'timeout_in_seconds': 5,
'test_name': 'algebraic_tests'},
'test_results': {'number_of_variables': [128],
'number_of_equations': [198],
'number_of_monomials': [296],
'test_results': {'number_of_variables': [104],
'number_of_equations': [174],
'number_of_monomials': [272],
'max_degree_of_equations': [2],
'test_passed': [False]}}

Expand Down Expand Up @@ -234,7 +234,7 @@ def test_is_spn():

def test_polynomial_system():
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'
assert str(tea.polynomial_system()) == 'Polynomial Sequence with 288 Polynomials in 384 Variables'


def test_polynomial_system_at_round():
Expand Down
Loading