Python/ciphers/rail_fence_cipher.py

103 lines
3.1 KiB
Python
Raw Permalink Normal View History

""" https://en.wikipedia.org/wiki/Rail_fence_cipher """
def encrypt(input_string: str, key: int) -> str:
"""
Shuffles the character of a string by placing each of them
in a grid (the height is dependent on the key) in a zigzag
formation and reading it left to right.
>>> encrypt("Hello World", 4)
'HWe olordll'
>>> encrypt("This is a message", 0)
Traceback (most recent call last):
...
ValueError: Height of grid can't be 0 or negative
>>> encrypt(b"This is a byte string", 5)
Traceback (most recent call last):
...
TypeError: sequence item 0: expected str instance, int found
"""
temp_grid: list[list[str]] = [[] for _ in range(key)]
lowest = key - 1
if key <= 0:
raise ValueError("Height of grid can't be 0 or negative")
if key == 1 or len(input_string) <= key:
return input_string
for position, character in enumerate(input_string):
num = position % (lowest * 2) # puts it in bounds
num = min(num, lowest * 2 - num) # creates zigzag pattern
temp_grid[num].append(character)
grid = ["".join(row) for row in temp_grid]
output_string = "".join(grid)
return output_string
def decrypt(input_string: str, key: int) -> str:
"""
Generates a template based on the key and fills it in with
the characters of the input string and then reading it in
a zigzag formation.
>>> decrypt("HWe olordll", 4)
'Hello World'
>>> decrypt("This is a message", -10)
Traceback (most recent call last):
...
ValueError: Height of grid can't be 0 or negative
>>> decrypt("My key is very big", 100)
'My key is very big'
"""
grid = []
lowest = key - 1
if key <= 0:
raise ValueError("Height of grid can't be 0 or negative")
if key == 1:
return input_string
temp_grid: list[list[str]] = [[] for _ in range(key)] # generates template
for position in range(len(input_string)):
num = position % (lowest * 2) # puts it in bounds
num = min(num, lowest * 2 - num) # creates zigzag pattern
temp_grid[num].append("*")
counter = 0
for row in temp_grid: # fills in the characters
splice = input_string[counter : counter + len(row)]
grid.append(list(splice))
counter += len(row)
output_string = "" # reads as zigzag
for position in range(len(input_string)):
num = position % (lowest * 2) # puts it in bounds
num = min(num, lowest * 2 - num) # creates zigzag pattern
output_string += grid[num][0]
grid[num].pop(0)
return output_string
def bruteforce(input_string: str) -> dict[int, str]:
"""Uses decrypt function by guessing every key
>>> bruteforce("HWe olordll")[4]
'Hello World'
"""
results = {}
for key_guess in range(1, len(input_string)): # tries every key
results[key_guess] = decrypt(input_string, key_guess)
return results
if __name__ == "__main__":
import doctest
doctest.testmod()