def textToBytes(text): """Converts a string into a list of ASCII codes.""" numbers = [] for letter in text: number = ord(letter) numbers.append(number) return numbers def bytesToText(numbers): """Converts a number list into text using ASCII decoding.""" text = "" for number in numbers: letter = chr(number) text += letter return text def binaryToBytes(binary): """Converts a sequence of binary strings into a list of numbers.""" numbers = [] for bin in binary: number = int(bin, 2) numbers.append(number) return numbers def bytesToBinary(numbers): """Converts a number list into a space separated binary string.""" text = "" for number in numbers: binary = format(number, "b") text += binary + " " return text def parseWords(binaryText): """Splits a string with space-separated words into a list of words.""" return binaryText.split() def xor(block, key): """Computes XOR for a single block of numbers.""" result = [] for i in range(len(block)): result.append(block[i] ^ key[i]) return result def decryptBlock(block, key, previous_block): return xor(xor(block, key), previous_block) def decrypt(ciphertext, passphrase): """Decrypts a sequence of binary text using a passphrase""" ciphernumbers = binaryToBytes(parseWords(ciphertext)) key = textToBytes(passphrase) plaintext = [] # Content does not matter - we'll discard the first block previous_block = textToBytes("1234567890") # Split ciphertext into blocks of key length, then decrypt each block. first = True for i in range(0, len(ciphernumbers), len(key)): cipherblock = ciphernumbers[i:i+len(key)] plainblock = decryptBlock(cipherblock, key, previous_block) if first: # Discard the first block. first = False else: plaintext += plainblock previous_block = cipherblock return bytesToText(plaintext) def encryptBlock(block, key, previous_block): return xor(xor(block, previous_block), key) def encrypt(plaintext, passphrase): """Encrypts plaintext as binary string output using passphrase.""" key = textToBytes(passphrase) plaincodes = textToBytes(plaintext) # First block is random - decrypt will discard it. first_block = textToBytes("abcdefghij") # Initialization vector - in reality, we'd choose a random value. initialization_vector = textToBytes("9876543210") # Encrypt the random first block with the init vector - it will be # discarded. ciphertext = encryptBlock(first_block, key, initialization_vector) previous_block = ciphertext # Split plaintext into blocks of key length, then encrypt each block. for i in range(0, len(plaincodes), len(key)): plainblock = plaincodes[i:i+len(key)] cipherblock = encryptBlock(plainblock, key, previous_block) ciphertext += cipherblock previous_block = cipherblock return bytesToBinary(ciphertext) key = "ROMANSHORN" print(decrypt(encrypt("Das ist alles wirlich sehr, sehr geheim!", key), key))