#!/usr/bin/env python3 from random import randint from math import log2 class lfsr(object): def __init__(self, state, taps): self.state = state self.taps = taps # getters and setters (private attributes) def get_state(self): return self.__state def set_state(self, value): self.__state = value state = property(fget=get_state, fset=set_state, doc="lfsr state (current bits value in lfsr)") def get_taps(self): return self.__taps def set_taps(self, valeur): self.__taps = valeur taps = property(fget=get_taps, fset=set_taps, doc="lfsr taps (bit positions affecting next state)") def shift(self): ''' calculate next state and return the output bit ''' feedback = sum(self.state[tap] for tap in self.taps) % 2 output = self.state[0] self.state = self.state[1:] + [feedback] return output def test_lfsr17(): print("test lfsr17") key = [randint(0, 1) for _ in range(16)] # first 16 bits key.append(1) # prevent initial state from being {0}^17 taps = [0, 14] lfsr17 = lfsr(key, taps) states = [lfsr17.state] for _ in range(2**17-2): lfsr17.shift() states.append(lfsr17.state) sorted_states = sorted(states, key=lambda x: tuple(x)) for i in range(2**17-2): if sorted_states[i] == sorted_states[i+1]: # compare each state with the next state in the sorted list, if 2 states are identical they should be next to each other print(f'state {sorted_states[i]} appears at least 2 times') return False n_state = len(sorted_states) n_state_log = log2(n_state+1) print(f'all {n_state} = 2^({n_state_log})-1 generated states are different') return True def css_encrypt(text, key): taps17 = [0, 14] lfsr17 = lfsr((key[:16]+[1]), taps17) taps25 = [0, 3, 4, 12] lfsr25 = lfsr((key[16:]+[1]), taps25) cipher = 0 carry = 0 if isinstance(text, int): Bytes = text.to_bytes((text.bit_length() + 7) // 8, 'big') elif isinstance(text, bytes): Bytes = text else: raise TypeError("input text should be bytes or integer") for byte in Bytes: x_b2 = "" y_b2 = "" # Generate bytes from lfsr17 and lfsr25 for _ in range(8): x_b2 += str(lfsr17.shift()) y_b2 += str(lfsr25.shift()) x = int(x_b2[::-1], 2) y = int(y_b2[::-1], 2) z = (x + y + carry) % 256 carry = 1 if x + y > 255 else 0 cipher_byte = z ^ byte # print(cipher_byte.to_bytes((cipher_byte.bit_length() + 7) // 8, 'big')) cipher = (cipher << 8) | cipher_byte cipher_bytes = b'\x00'*(len(Bytes) - len(cipher.to_bytes((cipher.bit_length() + 7) // 8, 'big'))) +cipher.to_bytes((cipher.bit_length() + 7) // 8, 'big') # padding return cipher_bytes def test_encrypt(): print("test encryption: text 0xffffffffff, key 0x0 ∈ {0, 1}^40") cipher = int.from_bytes(css_encrypt(0xffffffffff, [0]*40), byteorder='big') print(f'cipher: {hex(cipher)}') clear = int.from_bytes(css_encrypt(cipher, [0]*40)) print(f'decrypted: {hex(clear)}') print(f'original text and decrypted message are the same: {clear==0xffffffffff}') def gen_6_bytes(): # key = [randint(0, 1) for _ in range(40)] # key = [1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0] key = [0]*40 text = b'\x00\x00\x00\x00\x00\x00' print(key) cipher = css_encrypt(text, key) print(cipher) print(css_encrypt(cipher, key)) return cipher test_lfsr17() print() test_encrypt() print() gen_6_bytes()