This is a Python implementation of a stream cipher encryption algorithm. This implementation originates from the one displayed on the TI-Basic wikidot cryptography page.
I am well aware that this code breaks the holy 80 character limit, so any suggestion on how to make the code more succinct would be appreciated. My main concern, however, is the security of the random
module; I know that it's stated in the documentation that the module is not cryptographically secure, so I would appreciate alternatives which provide the same functionality.
My primary concern is security, however, I am also concerned about readability.
import random
def Crypt(string,key,encrypt=1):
"""
This program will input a message, a key, and the
decision to encrypt or decrypt the message.
It will then output the ciphertext created by running
the message through a stream cipher encryption algorithm.
What is a stream cipher encryption algorithm?:
A stream cipher encryption algorithm shifts each letter
in the message along the alphabet by a pseudorandom
amount determined by the inputted key.
"""
random.seed(key)
# This must be *2 so a letter shifted beyond the end of the alphabet will loop back to the beginning
alphabet = 2 * " AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890.;:,'?/|{}[]-=+_!@#$%^&*()<>`~"
# I declare this in a variable so the program can work with a variable length alphabet
lenalpha = int(len(alphabet)/2)
if encrypt:
# This will shift each letter up the alphabet by a pseudo-random amount based on the key
return ''.join([alphabet[alphabet.index(string[p]) + lenalpha - int(lenalpha * random.random())] for p in range(len(string))])
else:
# This will shift each letter down the alphabet by a pseudo-random amount based on the key
return ''.join([alphabet[alphabet.index(string[p]) - lenalpha + int(lenalpha * random.random())] for p in range(len(string))])
if __name__ == '__main__':
message = input("Input your message: ")
key = input("Input a key: ")
finished_with_the_user = False
while not finished_with_the_user:
encrypt_question = input("Encrypt or decrypt a message?(1,0): ")
if encrypt_question.isdigit():
finished_with_the_user = True
encrypt_question = int(encrypt_question)
else:
print("Please input a valid number.")
print(Crypt(message, key, encrypt_question))
input("Press enter to exit.")
Any suggested edits and edits that I think of will be applied below.
import random
import string
def shift(current_position, distance, direction: (0, 1)):
direction = 1 if direction else -1
return current_position + direction * distance
def stream_cipher(message, key, do_encrypt=True):
"""
This function will receive a message, a key, and the
decision to encrypt or decrypt the message.
The function uses a stream cipher encryption algorithm
which replaces each letter in the message with a
pseudo-random character from a given character set.
Example:
>>> stream_cipher("This is a test", 1234)
'wPwV~#5;:D"905'
>>> stream_cipher('wPwV~#5;:D"905', 1234, False)
'This is a test'
"""
random.seed(key)
# The character set must be multiplied by 2 so
# a character shifted beyond the end of the
# character set will loop back to the beginning.
characters = 2 * (
string.ascii_letters +
string.digits +
string.punctuation + ' '
)
# I declare this in a variable so the
# program can work with a variable length character set
lenchars = len(characters)//2
# This will replace each character in the message
# with a pseudo-random character selected from
# the character set.
return ''.join([characters[shift(characters.index(message[each_char]), lenchars - int(lenchars * random.random()), do_encrypt)] for each_char in range(len(message))])
def main():
message = input("Input a message: ")
key = input("Input a key: ")
while True:
do_encrypt = input("Encrypt or decrypt the message? (1,0): ")
if do_encrypt in ('1', '0'):
break
print("Please input a valid number,\n"
"either 0 for decryption or 1 for encryption.")
print(encrypt(message, key, do_encrypt == '1'))
input("Press enter to exit.")
if __name__ == '__main__':
main()