fix(mypy): type annotations for cipher algorithms (#4306)

* fix(mypy): type annotations for cipher algorithms

* Update mypy workflow to include cipher directory

* fix: mypy errors in hill_cipher.py

* fix build errors
This commit is contained in:
Dhruv Manilawala 2021-04-04 10:52:12 +05:30 committed by GitHub
parent 806b3864c3
commit 60895366c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 207 additions and 210 deletions

View File

@ -23,7 +23,7 @@ jobs:
python -m pip install mypy pytest-cov -r requirements.txt python -m pip install mypy pytest-cov -r requirements.txt
# FIXME: #4052 fix mypy errors in the exclude directories and remove them below # FIXME: #4052 fix mypy errors in the exclude directories and remove them below
- run: mypy --ignore-missing-imports - run: mypy --ignore-missing-imports
--exclude '(ciphers|conversions|data_structures|digital_image_processing|dynamic_programming|graphs|linear_algebra|maths|matrix|other|project_euler|scripts|searches|strings*)/$' . --exclude '(conversions|data_structures|digital_image_processing|dynamic_programming|graphs|linear_algebra|maths|matrix|other|project_euler|scripts|searches|strings*)/$' .
- name: Run tests - name: Run tests
run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. .
- if: ${{ success() }} - if: ${{ success() }}

View File

@ -241,9 +241,7 @@ class DiffieHellman:
return sha256(str(shared_key).encode()).hexdigest() return sha256(str(shared_key).encode()).hexdigest()
@staticmethod @staticmethod
def is_valid_public_key_static( def is_valid_public_key_static(remote_public_key_str: int, prime: int) -> bool:
local_private_key_str: str, remote_public_key_str: str, prime: int
) -> bool:
# check if the other public key is valid based on NIST SP800-56 # check if the other public key is valid based on NIST SP800-56
if 2 <= remote_public_key_str and remote_public_key_str <= prime - 2: if 2 <= remote_public_key_str and remote_public_key_str <= prime - 2:
if pow(remote_public_key_str, (prime - 1) // 2, prime) == 1: if pow(remote_public_key_str, (prime - 1) // 2, prime) == 1:
@ -257,9 +255,7 @@ class DiffieHellman:
local_private_key = int(local_private_key_str, base=16) local_private_key = int(local_private_key_str, base=16)
remote_public_key = int(remote_public_key_str, base=16) remote_public_key = int(remote_public_key_str, base=16)
prime = primes[group]["prime"] prime = primes[group]["prime"]
if not DiffieHellman.is_valid_public_key_static( if not DiffieHellman.is_valid_public_key_static(remote_public_key, prime):
local_private_key, remote_public_key, prime
):
raise ValueError("Invalid public key") raise ValueError("Invalid public key")
shared_key = pow(remote_public_key, local_private_key, prime) shared_key = pow(remote_public_key, local_private_key, prime)
return sha256(str(shared_key).encode()).hexdigest() return sha256(str(shared_key).encode()).hexdigest()

View File

@ -64,13 +64,12 @@ class HillCipher:
to_int = numpy.vectorize(lambda x: round(x)) to_int = numpy.vectorize(lambda x: round(x))
def __init__(self, encrypt_key: int): def __init__(self, encrypt_key: numpy.ndarray) -> None:
""" """
encrypt_key is an NxN numpy array encrypt_key is an NxN numpy array
""" """
self.encrypt_key = self.modulus(encrypt_key) # mod36 calc's on the encrypt key self.encrypt_key = self.modulus(encrypt_key) # mod36 calc's on the encrypt key
self.check_determinant() # validate the determinant of the encryption key self.check_determinant() # validate the determinant of the encryption key
self.decrypt_key = None
self.break_key = encrypt_key.shape[0] self.break_key = encrypt_key.shape[0]
def replace_letters(self, letter: str) -> int: def replace_letters(self, letter: str) -> int:
@ -139,8 +138,8 @@ class HillCipher:
for i in range(0, len(text) - self.break_key + 1, self.break_key): for i in range(0, len(text) - self.break_key + 1, self.break_key):
batch = text[i : i + self.break_key] batch = text[i : i + self.break_key]
batch_vec = [self.replace_letters(char) for char in batch] vec = [self.replace_letters(char) for char in batch]
batch_vec = numpy.array([batch_vec]).T batch_vec = numpy.array([vec]).T
batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[
0 0
] ]
@ -151,7 +150,7 @@ class HillCipher:
return encrypted return encrypted
def make_decrypt_key(self): def make_decrypt_key(self) -> numpy.ndarray:
""" """
>>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]]))
>>> hill_cipher.make_decrypt_key() >>> hill_cipher.make_decrypt_key()
@ -184,17 +183,15 @@ class HillCipher:
>>> hill_cipher.decrypt('85FF00') >>> hill_cipher.decrypt('85FF00')
'HELLOO' 'HELLOO'
""" """
self.decrypt_key = self.make_decrypt_key() decrypt_key = self.make_decrypt_key()
text = self.process_text(text.upper()) text = self.process_text(text.upper())
decrypted = "" decrypted = ""
for i in range(0, len(text) - self.break_key + 1, self.break_key): for i in range(0, len(text) - self.break_key + 1, self.break_key):
batch = text[i : i + self.break_key] batch = text[i : i + self.break_key]
batch_vec = [self.replace_letters(char) for char in batch] vec = [self.replace_letters(char) for char in batch]
batch_vec = numpy.array([batch_vec]).T batch_vec = numpy.array([vec]).T
batch_decrypted = self.modulus(self.decrypt_key.dot(batch_vec)).T.tolist()[ batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0]
0
]
decrypted_batch = "".join( decrypted_batch = "".join(
self.replace_digits(num) for num in batch_decrypted self.replace_digits(num) for num in batch_decrypted
) )
@ -203,12 +200,12 @@ class HillCipher:
return decrypted return decrypted
def main(): def main() -> None:
N = int(input("Enter the order of the encryption key: ")) N = int(input("Enter the order of the encryption key: "))
hill_matrix = [] hill_matrix = []
print("Enter each row of the encryption key with space separated integers") print("Enter each row of the encryption key with space separated integers")
for i in range(N): for _ in range(N):
row = [int(x) for x in input().split()] row = [int(x) for x in input().split()]
hill_matrix.append(row) hill_matrix.append(row)

View File

@ -29,8 +29,8 @@ def mixed_keyword(key: str = "college", pt: str = "UNIVERSITY") -> str:
# print(temp) # print(temp)
alpha = [] alpha = []
modalpha = [] modalpha = []
for i in range(65, 91): for j in range(65, 91):
t = chr(i) t = chr(j)
alpha.append(t) alpha.append(t)
if t not in temp: if t not in temp:
temp.append(t) temp.append(t)
@ -38,23 +38,23 @@ def mixed_keyword(key: str = "college", pt: str = "UNIVERSITY") -> str:
r = int(26 / 4) r = int(26 / 4)
# print(r) # print(r)
k = 0 k = 0
for i in range(r): for _ in range(r):
t = [] s = []
for j in range(len_temp): for j in range(len_temp):
t.append(temp[k]) s.append(temp[k])
if not (k < 25): if not (k < 25):
break break
k += 1 k += 1
modalpha.append(t) modalpha.append(s)
# print(modalpha) # print(modalpha)
d = {} d = {}
j = 0 j = 0
k = 0 k = 0
for j in range(len_temp): for j in range(len_temp):
for i in modalpha: for m in modalpha:
if not (len(i) - 1 >= j): if not (len(m) - 1 >= j):
break break
d[alpha[k]] = i[j] d[alpha[k]] = m[j]
if not k < 25: if not k < 25:
break break
k += 1 k += 1

View File

@ -1,7 +1,11 @@
from typing import Literal
LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def translate_message(key, message, mode): def translate_message(
key: str, message: str, mode: Literal["encrypt", "decrypt"]
) -> str:
""" """
>>> translate_message("QWERTYUIOPASDFGHJKLZXCVBNM","Hello World","encrypt") >>> translate_message("QWERTYUIOPASDFGHJKLZXCVBNM","Hello World","encrypt")
'Pcssi Bidsm' 'Pcssi Bidsm'
@ -40,7 +44,7 @@ def decrypt_message(key: str, message: str) -> str:
return translate_message(key, message, "decrypt") return translate_message(key, message, "decrypt")
def main(): def main() -> None:
message = "Hello World" message = "Hello World"
key = "QWERTYUIOPASDFGHJKLZXCVBNM" key = "QWERTYUIOPASDFGHJKLZXCVBNM"
mode = "decrypt" # set to 'encrypt' or 'decrypt' mode = "decrypt" # set to 'encrypt' or 'decrypt'

View File

@ -83,7 +83,7 @@ def decrypt(message: str) -> str:
return decipher return decipher
def main(): def main() -> None:
message = "Morse code here" message = "Morse code here"
result = encrypt(message.upper()) result = encrypt(message.upper())
print(result) print(result)

View File

@ -2,7 +2,8 @@ import random
class Onepad: class Onepad:
def encrypt(self, text: str) -> ([str], [int]): @staticmethod
def encrypt(text: str) -> tuple[list[int], list[int]]:
"""Function to encrypt text using pseudo-random numbers""" """Function to encrypt text using pseudo-random numbers"""
plain = [ord(i) for i in text] plain = [ord(i) for i in text]
key = [] key = []
@ -14,14 +15,14 @@ class Onepad:
key.append(k) key.append(k)
return cipher, key return cipher, key
def decrypt(self, cipher: [str], key: [int]) -> str: @staticmethod
def decrypt(cipher: list[int], key: list[int]) -> str:
"""Function to decrypt text using pseudo-random numbers.""" """Function to decrypt text using pseudo-random numbers."""
plain = [] plain = []
for i in range(len(key)): for i in range(len(key)):
p = int((cipher[i] - (key[i]) ** 2) / key[i]) p = int((cipher[i] - (key[i]) ** 2) / key[i])
plain.append(chr(p)) plain.append(chr(p))
plain = "".join([i for i in plain]) return "".join([i for i in plain])
return plain
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,8 +1,9 @@
import itertools import itertools
import string import string
from typing import Generator, Iterable
def chunker(seq, size): def chunker(seq: Iterable[str], size: int) -> Generator[tuple[str, ...], None, None]:
it = iter(seq) it = iter(seq)
while True: while True:
chunk = tuple(itertools.islice(it, size)) chunk = tuple(itertools.islice(it, size))
@ -37,7 +38,7 @@ def prepare_input(dirty: str) -> str:
return clean return clean
def generate_table(key: str) -> [str]: def generate_table(key: str) -> list[str]:
# I and J are used interchangeably to allow # I and J are used interchangeably to allow
# us to use a 5x5 table (25 letters) # us to use a 5x5 table (25 letters)

View File

@ -28,7 +28,7 @@ alphabet = {
} }
def generate_table(key: str) -> [(str, str)]: def generate_table(key: str) -> list[tuple[str, str]]:
""" """
>>> generate_table('marvin') # doctest: +NORMALIZE_WHITESPACE >>> generate_table('marvin') # doctest: +NORMALIZE_WHITESPACE
[('ABCDEFGHIJKLM', 'UVWXYZNOPQRST'), ('ABCDEFGHIJKLM', 'NOPQRSTUVWXYZ'), [('ABCDEFGHIJKLM', 'UVWXYZNOPQRST'), ('ABCDEFGHIJKLM', 'NOPQRSTUVWXYZ'),
@ -60,30 +60,21 @@ def decrypt(key: str, words: str) -> str:
return encrypt(key, words) return encrypt(key, words)
def get_position(table: [(str, str)], char: str) -> (int, int) or (None, None): def get_position(table: tuple[str, str], char: str) -> tuple[int, int]:
""" """
>>> table = [ >>> get_position(generate_table('marvin')[0], 'M')
... ('ABCDEFGHIJKLM', 'UVWXYZNOPQRST'), ('ABCDEFGHIJKLM', 'NOPQRSTUVWXYZ'), (0, 12)
... ('ABCDEFGHIJKLM', 'STUVWXYZNOPQR'), ('ABCDEFGHIJKLM', 'QRSTUVWXYZNOP'),
... ('ABCDEFGHIJKLM', 'WXYZNOPQRSTUV'), ('ABCDEFGHIJKLM', 'UVWXYZNOPQRST')]
>>> get_position(table, 'A')
(None, None)
""" """
if char in table[0]: # `char` is either in the 0th row or the 1st row
row = 0 row = 0 if char in table[0] else 1
else: col = table[row].index(char)
row = 1 if char in table[1] else -1 return row, col
return (None, None) if row == -1 else (row, table[row].index(char))
def get_opponent(table: [(str, str)], char: str) -> str: def get_opponent(table: tuple[str, str], char: str) -> str:
""" """
>>> table = [ >>> get_opponent(generate_table('marvin')[0], 'M')
... ('ABCDEFGHIJKLM', 'UVWXYZNOPQRST'), ('ABCDEFGHIJKLM', 'NOPQRSTUVWXYZ'), 'T'
... ('ABCDEFGHIJKLM', 'STUVWXYZNOPQR'), ('ABCDEFGHIJKLM', 'QRSTUVWXYZNOP'),
... ('ABCDEFGHIJKLM', 'WXYZNOPQRSTUV'), ('ABCDEFGHIJKLM', 'UVWXYZNOPQRST')]
>>> get_opponent(table, 'A')
'A'
""" """
row, col = get_position(table, char.upper()) row, col = get_position(table, char.upper())
if row == 1: if row == 1:
@ -97,14 +88,16 @@ if __name__ == "__main__":
doctest.testmod() # Fist ensure that all our tests are passing... doctest.testmod() # Fist ensure that all our tests are passing...
""" """
ENTER KEY: marvin Demo:
ENTER TEXT TO ENCRYPT: jessica
ENCRYPTED: QRACRWU Enter key: marvin
DECRYPTED WITH KEY: JESSICA Enter text to encrypt: jessica
Encrypted: QRACRWU
Decrypted with key: JESSICA
""" """
key = input("ENTER KEY: ").strip() key = input("Enter key: ").strip()
text = input("ENTER TEXT TO ENCRYPT: ").strip() text = input("Enter text to encrypt: ").strip()
cipher_text = encrypt(key, text) cipher_text = encrypt(key, text)
print(f"ENCRYPTED: {cipher_text}") print(f"Encrypted: {cipher_text}")
print(f"DECRYPTED WITH KEY: {decrypt(key, cipher_text)}") print(f"Decrypted with key: {decrypt(key, cipher_text)}")

View File

@ -20,7 +20,7 @@ def encrypt(input_string: str, key: int) -> str:
... ...
TypeError: sequence item 0: expected str instance, int found TypeError: sequence item 0: expected str instance, int found
""" """
grid = [[] for _ in range(key)] temp_grid: list[list[str]] = [[] for _ in range(key)]
lowest = key - 1 lowest = key - 1
if key <= 0: if key <= 0:
@ -31,8 +31,8 @@ def encrypt(input_string: str, key: int) -> str:
for position, character in enumerate(input_string): for position, character in enumerate(input_string):
num = position % (lowest * 2) # puts it in bounds num = position % (lowest * 2) # puts it in bounds
num = min(num, lowest * 2 - num) # creates zigzag pattern num = min(num, lowest * 2 - num) # creates zigzag pattern
grid[num].append(character) temp_grid[num].append(character)
grid = ["".join(row) for row in grid] grid = ["".join(row) for row in temp_grid]
output_string = "".join(grid) output_string = "".join(grid)
return output_string return output_string
@ -63,7 +63,7 @@ def decrypt(input_string: str, key: int) -> str:
if key == 1: if key == 1:
return input_string return input_string
temp_grid = [[] for _ in range(key)] # generates template temp_grid: list[list[str]] = [[] for _ in range(key)] # generates template
for position in range(len(input_string)): for position in range(len(input_string)):
num = position % (lowest * 2) # puts it in bounds num = position % (lowest * 2) # puts it in bounds
num = min(num, lowest * 2 - num) # creates zigzag pattern num = min(num, lowest * 2 - num) # creates zigzag pattern
@ -84,7 +84,7 @@ def decrypt(input_string: str, key: int) -> str:
return output_string return output_string
def bruteforce(input_string: str) -> dict: def bruteforce(input_string: str) -> dict[int, str]:
"""Uses decrypt function by guessing every key """Uses decrypt function by guessing every key
>>> bruteforce("HWe olordll")[4] >>> bruteforce("HWe olordll")[4]

View File

@ -20,7 +20,7 @@ def dencrypt(s: str, n: int = 13) -> str:
return out return out
def main(): def main() -> None:
s0 = input("Enter message: ") s0 = input("Enter message: ")
s1 = dencrypt(s0, 13) s1 = dencrypt(s0, 13)

View File

@ -7,7 +7,114 @@ DEFAULT_BLOCK_SIZE = 128
BYTE_SIZE = 256 BYTE_SIZE = 256
def main(): def get_blocks_from_text(
message: str, block_size: int = DEFAULT_BLOCK_SIZE
) -> list[int]:
message_bytes = message.encode("ascii")
block_ints = []
for block_start in range(0, len(message_bytes), block_size):
block_int = 0
for i in range(block_start, min(block_start + block_size, len(message_bytes))):
block_int += message_bytes[i] * (BYTE_SIZE ** (i % block_size))
block_ints.append(block_int)
return block_ints
def get_text_from_blocks(
block_ints: list[int], message_length: int, block_size: int = DEFAULT_BLOCK_SIZE
) -> str:
message: list[str] = []
for block_int in block_ints:
block_message: list[str] = []
for i in range(block_size - 1, -1, -1):
if len(message) + i < message_length:
ascii_number = block_int // (BYTE_SIZE ** i)
block_int = block_int % (BYTE_SIZE ** i)
block_message.insert(0, chr(ascii_number))
message.extend(block_message)
return "".join(message)
def encrypt_message(
message: str, key: tuple[int, int], blockSize: int = DEFAULT_BLOCK_SIZE
) -> list[int]:
encrypted_blocks = []
n, e = key
for block in get_blocks_from_text(message, blockSize):
encrypted_blocks.append(pow(block, e, n))
return encrypted_blocks
def decrypt_message(
encrypted_blocks: list[int],
message_length: int,
key: tuple[int, int],
block_size: int = DEFAULT_BLOCK_SIZE,
) -> str:
decrypted_blocks = []
n, d = key
for block in encrypted_blocks:
decrypted_blocks.append(pow(block, d, n))
return get_text_from_blocks(decrypted_blocks, message_length, block_size)
def read_key_file(key_filename: str) -> tuple[int, int, int]:
with open(key_filename) as fo:
content = fo.read()
key_size, n, EorD = content.split(",")
return (int(key_size), int(n), int(EorD))
def encrypt_and_write_to_file(
message_filename: str,
key_filename: str,
message: str,
block_size: int = DEFAULT_BLOCK_SIZE,
) -> str:
key_size, n, e = read_key_file(key_filename)
if key_size < block_size * 8:
sys.exit(
"ERROR: Block size is %s bits and key size is %s bits. The RSA cipher "
"requires the block size to be equal to or greater than the key size. "
"Either decrease the block size or use different keys."
% (block_size * 8, key_size)
)
encrypted_blocks = [str(i) for i in encrypt_message(message, (n, e), block_size)]
encrypted_content = ",".join(encrypted_blocks)
encrypted_content = f"{len(message)}_{block_size}_{encrypted_content}"
with open(message_filename, "w") as fo:
fo.write(encrypted_content)
return encrypted_content
def read_from_file_and_decrypt(message_filename: str, key_filename: str) -> str:
key_size, n, d = read_key_file(key_filename)
with open(message_filename) as fo:
content = fo.read()
message_length_str, block_size_str, encrypted_message = content.split("_")
message_length = int(message_length_str)
block_size = int(block_size_str)
if key_size < block_size * 8:
sys.exit(
"ERROR: Block size is %s bits and key size is %s bits. The RSA cipher "
"requires the block size to be equal to or greater than the key size. "
"Did you specify the correct key file and encrypted file?"
% (block_size * 8, key_size)
)
encrypted_blocks = []
for block in encrypted_message.split(","):
encrypted_blocks.append(int(block))
return decrypt_message(encrypted_blocks, message_length, (n, d), block_size)
def main() -> None:
filename = "encrypted_file.txt" filename = "encrypted_file.txt"
response = input(r"Encrypt\Decrypt [e\d]: ") response = input(r"Encrypt\Decrypt [e\d]: ")
@ -21,130 +128,23 @@ def main():
rkg.makeKeyFiles("rsa", 1024) rkg.makeKeyFiles("rsa", 1024)
message = input("\nEnter message: ") message = input("\nEnter message: ")
pubKeyFilename = "rsa_pubkey.txt" pubkey_filename = "rsa_pubkey.txt"
print("Encrypting and writing to %s..." % (filename)) print("Encrypting and writing to %s..." % (filename))
encryptedText = encryptAndWriteToFile(filename, pubKeyFilename, message) encryptedText = encrypt_and_write_to_file(filename, pubkey_filename, message)
print("\nEncrypted text:") print("\nEncrypted text:")
print(encryptedText) print(encryptedText)
elif mode == "decrypt": elif mode == "decrypt":
privKeyFilename = "rsa_privkey.txt" privkey_filename = "rsa_privkey.txt"
print("Reading from %s and decrypting..." % (filename)) print("Reading from %s and decrypting..." % (filename))
decryptedText = readFromFileAndDecrypt(filename, privKeyFilename) decrypted_text = read_from_file_and_decrypt(filename, privkey_filename)
print("writing decryption to rsa_decryption.txt...") print("writing decryption to rsa_decryption.txt...")
with open("rsa_decryption.txt", "w") as dec: with open("rsa_decryption.txt", "w") as dec:
dec.write(decryptedText) dec.write(decrypted_text)
print("\nDecryption:") print("\nDecryption:")
print(decryptedText) print(decrypted_text)
def getBlocksFromText(message: int, blockSize: int = DEFAULT_BLOCK_SIZE) -> [int]:
messageBytes = message.encode("ascii")
blockInts = []
for blockStart in range(0, len(messageBytes), blockSize):
blockInt = 0
for i in range(blockStart, min(blockStart + blockSize, len(messageBytes))):
blockInt += messageBytes[i] * (BYTE_SIZE ** (i % blockSize))
blockInts.append(blockInt)
return blockInts
def getTextFromBlocks(
blockInts: [int], messageLength: int, blockSize: int = DEFAULT_BLOCK_SIZE
) -> str:
message = []
for blockInt in blockInts:
blockMessage = []
for i in range(blockSize - 1, -1, -1):
if len(message) + i < messageLength:
asciiNumber = blockInt // (BYTE_SIZE ** i)
blockInt = blockInt % (BYTE_SIZE ** i)
blockMessage.insert(0, chr(asciiNumber))
message.extend(blockMessage)
return "".join(message)
def encryptMessage(
message: str, key: (int, int), blockSize: int = DEFAULT_BLOCK_SIZE
) -> [int]:
encryptedBlocks = []
n, e = key
for block in getBlocksFromText(message, blockSize):
encryptedBlocks.append(pow(block, e, n))
return encryptedBlocks
def decryptMessage(
encryptedBlocks: [int],
messageLength: int,
key: (int, int),
blockSize: int = DEFAULT_BLOCK_SIZE,
) -> str:
decryptedBlocks = []
n, d = key
for block in encryptedBlocks:
decryptedBlocks.append(pow(block, d, n))
return getTextFromBlocks(decryptedBlocks, messageLength, blockSize)
def readKeyFile(keyFilename: str) -> (int, int, int):
with open(keyFilename) as fo:
content = fo.read()
keySize, n, EorD = content.split(",")
return (int(keySize), int(n), int(EorD))
def encryptAndWriteToFile(
messageFilename: str,
keyFilename: str,
message: str,
blockSize: int = DEFAULT_BLOCK_SIZE,
) -> str:
keySize, n, e = readKeyFile(keyFilename)
if keySize < blockSize * 8:
sys.exit(
"ERROR: Block size is %s bits and key size is %s bits. The RSA cipher "
"requires the block size to be equal to or greater than the key size. "
"Either decrease the block size or use different keys."
% (blockSize * 8, keySize)
)
encryptedBlocks = encryptMessage(message, (n, e), blockSize)
for i in range(len(encryptedBlocks)):
encryptedBlocks[i] = str(encryptedBlocks[i])
encryptedContent = ",".join(encryptedBlocks)
encryptedContent = f"{len(message)}_{blockSize}_{encryptedContent}"
with open(messageFilename, "w") as fo:
fo.write(encryptedContent)
return encryptedContent
def readFromFileAndDecrypt(messageFilename: str, keyFilename: str) -> str:
keySize, n, d = readKeyFile(keyFilename)
with open(messageFilename) as fo:
content = fo.read()
messageLength, blockSize, encryptedMessage = content.split("_")
messageLength = int(messageLength)
blockSize = int(blockSize)
if keySize < blockSize * 8:
sys.exit(
"ERROR: Block size is %s bits and key size is %s bits. The RSA cipher "
"requires the block size to be equal to or greater than the key size. "
"Did you specify the correct key file and encrypted file?"
% (blockSize * 8, keySize)
)
encryptedBlocks = []
for block in encryptedMessage.split(","):
encryptedBlocks.append(int(block))
return decryptMessage(encryptedBlocks, messageLength, (n, d), blockSize)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -13,7 +13,7 @@ import math
import random import random
def rsafactor(d: int, e: int, N: int) -> [int]: def rsafactor(d: int, e: int, N: int) -> list[int]:
""" """
This function returns the factors of N, where p*q=N This function returns the factors of N, where p*q=N
Return: [p, q] Return: [p, q]

View File

@ -1,5 +1,6 @@
import random import random
import string import string
from typing import Optional
class ShuffledShiftCipher: class ShuffledShiftCipher:
@ -26,7 +27,7 @@ class ShuffledShiftCipher:
cip2 = ShuffledShiftCipher() cip2 = ShuffledShiftCipher()
""" """
def __init__(self, passcode: str = None): def __init__(self, passcode: Optional[str] = None) -> None:
""" """
Initializes a cipher object with a passcode as it's entity Initializes a cipher object with a passcode as it's entity
Note: No new passcode is generated if user provides a passcode Note: No new passcode is generated if user provides a passcode
@ -36,13 +37,13 @@ class ShuffledShiftCipher:
self.__key_list = self.__make_key_list() self.__key_list = self.__make_key_list()
self.__shift_key = self.__make_shift_key() self.__shift_key = self.__make_shift_key()
def __str__(self): def __str__(self) -> str:
""" """
:return: passcode of the cipher object :return: passcode of the cipher object
""" """
return "Passcode is: " + "".join(self.__passcode) return "Passcode is: " + "".join(self.__passcode)
def __neg_pos(self, iterlist: list) -> list: def __neg_pos(self, iterlist: list[int]) -> list[int]:
""" """
Mutates the list by changing the sign of each alternate element Mutates the list by changing the sign of each alternate element
@ -54,7 +55,7 @@ class ShuffledShiftCipher:
iterlist[i] *= -1 iterlist[i] *= -1
return iterlist return iterlist
def __passcode_creator(self) -> list: def __passcode_creator(self) -> list[str]:
""" """
Creates a random password from the selection buffer of Creates a random password from the selection buffer of
1. uppercase letters of the English alphabet 1. uppercase letters of the English alphabet
@ -65,10 +66,10 @@ class ShuffledShiftCipher:
:return: a password of a random length between 10 to 20 :return: a password of a random length between 10 to 20
""" """
choices = string.ascii_letters + string.digits choices = string.ascii_letters + string.digits
password = [random.choice(choices) for i in range(random.randint(10, 20))] password = [random.choice(choices) for _ in range(random.randint(10, 20))]
return password return password
def __make_key_list(self) -> list: def __make_key_list(self) -> list[str]:
""" """
Shuffles the ordered character choices by pivoting at breakpoints Shuffles the ordered character choices by pivoting at breakpoints
Breakpoints are the set of characters in the passcode Breakpoints are the set of characters in the passcode
@ -99,7 +100,7 @@ class ShuffledShiftCipher:
# creates points known as breakpoints to break the key_list_options at those # creates points known as breakpoints to break the key_list_options at those
# points and pivot each substring # points and pivot each substring
breakpoints = sorted(set(self.__passcode)) breakpoints = sorted(set(self.__passcode))
temp_list = [] temp_list: list[str] = []
# algorithm for creating a new shuffled list, keys_l, out of key_list_options # algorithm for creating a new shuffled list, keys_l, out of key_list_options
for i in key_list_options: for i in key_list_options:
@ -109,7 +110,7 @@ class ShuffledShiftCipher:
# keys_l # keys_l
if i in breakpoints or i == key_list_options[-1]: if i in breakpoints or i == key_list_options[-1]:
keys_l.extend(temp_list[::-1]) keys_l.extend(temp_list[::-1])
temp_list = [] temp_list.clear()
# returning a shuffled keys_l to prevent brute force guessing of shift key # returning a shuffled keys_l to prevent brute force guessing of shift key
return keys_l return keys_l
@ -167,7 +168,7 @@ class ShuffledShiftCipher:
return encoded_message return encoded_message
def test_end_to_end(msg: str = "Hello, this is a modified Caesar cipher"): def test_end_to_end(msg: str = "Hello, this is a modified Caesar cipher") -> str:
""" """
>>> test_end_to_end() >>> test_end_to_end()
'Hello, this is a modified Caesar cipher' 'Hello, this is a modified Caesar cipher'

View File

@ -15,7 +15,7 @@ def remove_duplicates(key: str) -> str:
return key_no_dups return key_no_dups
def create_cipher_map(key: str) -> dict: def create_cipher_map(key: str) -> dict[str, str]:
""" """
Returns a cipher map given a keyword. Returns a cipher map given a keyword.
:param key: keyword to use :param key: keyword to use
@ -40,7 +40,7 @@ def create_cipher_map(key: str) -> dict:
return cipher_alphabet return cipher_alphabet
def encipher(message: str, cipher_map: dict) -> str: def encipher(message: str, cipher_map: dict[str, str]) -> str:
""" """
Enciphers a message given a cipher map. Enciphers a message given a cipher map.
:param message: Message to encipher :param message: Message to encipher
@ -52,7 +52,7 @@ def encipher(message: str, cipher_map: dict) -> str:
return "".join(cipher_map.get(ch, ch) for ch in message.upper()) return "".join(cipher_map.get(ch, ch) for ch in message.upper())
def decipher(message: str, cipher_map: dict) -> str: def decipher(message: str, cipher_map: dict[str, str]) -> str:
""" """
Deciphers a message given a cipher map Deciphers a message given a cipher map
:param message: Message to decipher :param message: Message to decipher
@ -67,7 +67,7 @@ def decipher(message: str, cipher_map: dict) -> str:
return "".join(rev_cipher_map.get(ch, ch) for ch in message.upper()) return "".join(rev_cipher_map.get(ch, ch) for ch in message.upper())
def main(): def main() -> None:
""" """
Handles I/O Handles I/O
:return: void :return: void

View File

@ -4,7 +4,7 @@ import sys
LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def main(): def main() -> None:
message = input("Enter message: ") message = input("Enter message: ")
key = "LFWOAYUISVKMNXPBDCRJTQEGHZ" key = "LFWOAYUISVKMNXPBDCRJTQEGHZ"
resp = input("Encrypt/Decrypt [e/d]: ") resp = input("Encrypt/Decrypt [e/d]: ")
@ -68,7 +68,7 @@ def translateMessage(key: str, message: str, mode: str) -> str:
return translated return translated
def getRandomKey(): def getRandomKey() -> str:
key = list(LETTERS) key = list(LETTERS)
random.shuffle(key) random.shuffle(key)
return "".join(key) return "".join(key)

View File

@ -1,7 +1,7 @@
# https://en.wikipedia.org/wiki/Trifid_cipher # https://en.wikipedia.org/wiki/Trifid_cipher
def __encryptPart(messagePart: str, character2Number: dict) -> str: def __encryptPart(messagePart: str, character2Number: dict[str, str]) -> str:
one, two, three = "", "", "" one, two, three = "", "", ""
tmp = [] tmp = []
@ -16,7 +16,9 @@ def __encryptPart(messagePart: str, character2Number: dict) -> str:
return one + two + three return one + two + three
def __decryptPart(messagePart: str, character2Number: dict) -> (str, str, str): def __decryptPart(
messagePart: str, character2Number: dict[str, str]
) -> tuple[str, str, str]:
tmp, thisPart = "", "" tmp, thisPart = "", ""
result = [] result = []
@ -32,7 +34,9 @@ def __decryptPart(messagePart: str, character2Number: dict) -> (str, str, str):
return result[0], result[1], result[2] return result[0], result[1], result[2]
def __prepare(message: str, alphabet: str) -> (str, str, dict, dict): def __prepare(
message: str, alphabet: str
) -> tuple[str, str, dict[str, str], dict[str, str]]:
# Validate message and alphabet, set to upper and remove spaces # Validate message and alphabet, set to upper and remove spaces
alphabet = alphabet.replace(" ", "").upper() alphabet = alphabet.replace(" ", "").upper()
message = message.replace(" ", "").upper() message = message.replace(" ", "").upper()

View File

@ -8,7 +8,7 @@ text. The type of transposition cipher demonstrated under is the ROUTE cipher.
""" """
def main(): def main() -> None:
message = input("Enter message: ") message = input("Enter message: ")
key = int(input("Enter key [2-%s]: " % (len(message) - 1))) key = int(input("Enter key [2-%s]: " % (len(message) - 1)))
mode = input("Encryption/Decryption [e/d]: ") mode = input("Encryption/Decryption [e/d]: ")

View File

@ -5,7 +5,7 @@ import time
from . import transposition_cipher as transCipher from . import transposition_cipher as transCipher
def main(): def main() -> None:
inputFile = "Prehistoric Men.txt" inputFile = "Prehistoric Men.txt"
outputFile = "Output.txt" outputFile = "Output.txt"
key = int(input("Enter key: ")) key = int(input("Enter key: "))

View File

@ -1,7 +1,7 @@
LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def main(): def main() -> None:
message = input("Enter message: ") message = input("Enter message: ")
key = input("Enter key [alphanumeric]: ") key = input("Enter key [alphanumeric]: ")
mode = input("Encrypt/Decrypt [e/d]: ") mode = input("Encrypt/Decrypt [e/d]: ")

View File

@ -28,7 +28,7 @@ class XORCipher:
# private field # private field
self.__key = key self.__key = key
def encrypt(self, content: str, key: int) -> [str]: def encrypt(self, content: str, key: int) -> list[str]:
""" """
input: 'content' of type string and 'key' of type int input: 'content' of type string and 'key' of type int
output: encrypted string 'content' as a list of chars output: encrypted string 'content' as a list of chars
@ -53,7 +53,7 @@ class XORCipher:
return ans return ans
def decrypt(self, content: str, key: int) -> [str]: def decrypt(self, content: str, key: int) -> list[str]:
""" """
input: 'content' of type list and 'key' of type int input: 'content' of type list and 'key' of type int
output: decrypted string 'content' as a list of chars output: decrypted string 'content' as a list of chars