ord
function is to convert a character to its equivalent integer representation.
chr
function is to convert the integer to its character representation.
random.choice()
method randomly picks a character from a series of characters to form a string of N length, this is the key for encryption and decryption.
A message is taken from user’s input, for each character in a message it is mapped together with a character of a key with the zip
function.
The zip object is a collection of a message’s character and key’s character pair, each pair is exclusively ORed and form a string.
itertools.cycle()
method is to cycle through the key string, hence there is no need to check the length of the key string.
Without itertools.cycle()
the key string length has to be more than or equal to the message otherwise the message will not be fully encrypted causing a lost of original message.
This is a simple demonstration if the key string length is shorter than the message length.
key = "secretkey" message = "this is my message, see if it can be encrypted completely." cipher_text = "".join(chr(ord(c1) ^ ord(c2)) for c1, c2 in zip(message, key)) print(cipher_text) plain_text = "".join(chr(ord(c1) ^ ord(c2)) for c1, c2 in zip(cipher_text, key)) print(plain_text)
The result is like this:
With itertools.cycle
the characters of the key string will be re-cycled until the entire message is completed.
from itertools import cycle key = "secretkey" message = "this is my message, see if it can be encrypted completely." cipher_text = "".join(chr(ord(c1) ^ ord(c2)) for c1, c2 in zip(message, cycle(key))) print(cipher_text) plain_text = "".join(chr(ord(c1) ^ ord(c2)) for c1, c2 in zip(cipher_text, cycle(key))) print(plain_text)
The result is the entire message is encrypted and preserved after decryption.
Package of all the functions to encrypt and decrypt message.
from itertools import cycle import random import string # The string of lower and upper case alphabets and all digits. str_types = string.ascii_lowercase + string.ascii_uppercase + string.digits # tuple of errors. errors = IOError, OSError def xor_fn(message, cipher_key): """ This is an Exclusive OR function :param message: plaintext or cipher text. :param cipher_key: key chosen by create_key function. :return: return a string either cipher text or plain text. """ return "".join(chr(ord(x) ^ ord(y)) for x, y in zip(message, cycle(cipher_key))) def create_key(key_len=1024): """ Create a key string picked from str_types object. :param key_len: The length of the key string, default is 1024 bytes. :return: return key string. """ return "".join(random.choice(str_types) for _ in range(0, key_len)) def write_file(filename, data): """ Write the string to file :param filename: filename :param data: the data to be written to the file. :return: Status. """ try: with open(filename, "w") as file: file.write(data) print(f"{filename} is written successfully.") return True except errors as e: print(e) return False except: # Generic errors that are not expected. print(f"Unknown error has occurred while writing to {filename}.") return False def read_file(filename): """ Open the specified file and read its contents. :param filename: filename :return: contents from the file. """ try: with open(filename, "r") as file: data = file.read() return data except errors as e: print(e) except: # Generic errors that are not expected. print(f"Unknown error has occurred while reading {filename}.")
So here is the example code that creates the key file, and then prompts user to enter a message for encryption, then after that decrypts the message.
from handmade_crypto import * import sys from os.path import exists if exists("secret.key"): message = input("Your secret message: ") crypto_key = read_file("secret.key") cipher = xor_fn(message, crypto_key) print(f"Cipher text is: {cipher}") write_file("ciphertext.txt", cipher) data = read_file("ciphertext.txt") plain = xor_fn(data, crypto_key) print(f"Decrypted text: {plain}") else: key = create_key() # Key length is 1024 bytes long. print(f"Key generated: {key}") if write_file("secret.key", key): print("Key is written to file.") else: print("Key has failed to write to file.") sys.exit(1)
So here is the result, as there is no key yet, a key is created on the first run.
The secret key string is this 8upGC890zAQeNOxdfgYHV77zpBIWouMWiTxBrNkX1bzsxPN8Xn8D9ic7Hjhf2QAdiOqmIHIJLByIuBo00ZXW4vUNMPvpiu8O8IWDAnY0F38oOi4bLWePlLMkRM4k3DcjSZ7VMUDzaYwM09O11ElRy0WGlEiRwCJ43VXw6vvwPRhQI1mKf34rthXRmqsKyk4qEx22WddcWgkjq2Nct4ZJoWX9Rd8dAVEGH3cVcpfn30vTryWePgb64ZV6VCj1wUHP1ffCmWBqDPDtZGHG45dwwWLwrbbBEWoqbyt5iXgaZOHFknE3h9qERCx51GzdTjK1B2PtObaE2WKQMvvcwefEj7dReJ73nR5BucaYvDcLEqzt2UAQpe4aH4vO123nI5lYStbyEyezVSvJz4G3woMGwVyADfk2jAx2IvnvaCzMDkwVtQaolTPlYwXLtkll0bynl9xqBhUrL1ywSxim3VsFqObHjyePquHduRXUbzOXCK8Jk7caICM5BOdNw18Kf3USS7FH2HwZ2mjsEYCvWCovfZwPrxfBULgVv3kYUSpAKGRnp4ChljflD7063eQqk47jGg5dr3LkkhpBccQkrpTpw7TXnCwOnS7UoIOIREVBykIDHsebglOf9i10Wyt5cs8sbVx3Mj7yye0SeXB0f1VGnveARUJrDRZE39y5kNn9P1kzrcCU9TsYscMy4T2yPoPRSZpWBhi6tYaExlmOeeQWmJtXncS1sRQb99KC9t9rftFJ2vajhyDYI4YfXtuJypcsxVrKK9sz8SuLEM3Q8Wa8Uqvfip4AyVBqxuSwp33KZFX1ft7PrPerJUweBa6f4Url5l57678xbHHqObZtu0NO2Hkqj8JpZRvauw9nXTWS07mNjD5yylkOV9EB5j72Dr5mzkEMQxisyJOyP1fPG7GVXkEOvsZvNKp16SD5Pjsif7BBwA0N1rlZ9wPeBDdDAYT0dAnyRy87CtThG4R97vjkizAsPpyIZAL668xkQ6glsKviAwEV8v1UNeUL5AzI0Jnc
which is 1024 characters long.
On the second run a prompt appears to get the message input from user then it encrypts and decrypts with the key file.