CSS-cipher/main.py

112 lines
3.6 KiB
Python
Raw Normal View History

2024-04-13 00:28:03 +02:00
#!/usr/bin/env python3
2024-04-21 15:28:24 +02:00
from random import randint
from math import log2
2024-04-13 00:28:03 +02:00
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
2024-04-21 17:39:00 +02:00
output = self.state[0]
self.state = self.state[1:] + [feedback]
2024-04-13 00:28:03 +02:00
return output
2024-04-21 15:28:24 +02:00
def test_lfsr17():
print("test lfsr17")
2024-04-21 15:28:24 +02:00
key = [randint(0, 1) for _ in range(16)] # first 16 bits
key.append(1) # prevent initial state from being {0}^17
2024-04-21 17:39:00 +02:00
taps = [0, 14]
2024-04-21 15:28:24 +02:00
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')
2024-04-21 15:28:24 +02:00
return True
2024-04-21 17:39:00 +02:00
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
2024-04-21 17:39:00 +02:00
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:
2024-04-21 17:39:00 +02:00
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)
2024-04-21 17:39:00 +02:00
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
2024-04-21 17:39:00 +02:00
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
2024-04-21 15:28:24 +02:00
test_lfsr17()
print()
test_encrypt()
print()
gen_6_bytes()