HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Simple stream cipher encryption/decryption function

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
streamencryptionsimplefunctiondecryptioncipher

Problem

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 __nam

Solution

I'm focusing on readability for my answer. Reading Python's style guide, PEP0008, will be a great help. But I'll highlight specific areas in your code here.

Function names should always be lowercase. Classes should use PascalCase, so your Crypt will look like it's a class. Comma separated sequences in Python should have spaces after each comma, just like they would when writing an english sentence. It's more readable.

def crypt(string, key, encrypt=1):


encrypt shouldn't be an int. It should be a boolean. I know that you get user input for it, but just pass a check to see if the user entered 1 or not.

crypt(message, key, encrypt_question == '1')


You can get your alphabet string programmatically instead of typing all the characters. The string module can be imported and will provide you with ways to get the uppercase and lowercase letters, digits and punctuation marks of a user's locale. This also makes your script more compatible with different languages.
(Note: that if you use this you'll need to have a different name for the string parameter, I use often use s but single letter names are debateable)

alphabet = 2 * (string.letters + string.digits + string.punctuation + ' ')


On that note, alphabet is a bad name. You're including a lot more than the alphabet but the name makes it sound like numerical characters and punctuation are unsupported. characters is better, or chars.

You can use Python's floor division operator to avoid getting a float. It's just an extra slash and will make Python round down to an int. While I'm at it, add space on either side of operators like this. It's more readable.

lenalpha = len(alphabet) // 2


Don't repeat the comments here, they're practically identical anyway. Just make it less specific and put one before the if.

# Shift each letter along char by a pseudo-random amount based on the key
if encrypt:


Instead of while finished_with_the_user, just use while True and then break when the input is valid. Again about naming, is_encrypt, do_encrypt or maybe even encrypt would be better. The question part isn't helpful to know what the variable is for, it just tells you where the value came from, but we don't care about that. I also recommend making it clearer to the user what a valid input is if they provide a wrong one.

while True:
    encrypt_question = input("Encrypt or decrypt a message? (1,0): ")
    if encrypt_question in ('0', '1'):
        break
    print("Please input a valid number, either 0 for decrypt or 1 for encrypt.")

Code Snippets

def crypt(string, key, encrypt=1):
crypt(message, key, encrypt_question == '1')
alphabet = 2 * (string.letters + string.digits + string.punctuation + ' ')
lenalpha = len(alphabet) // 2
# Shift each letter along char by a pseudo-random amount based on the key
if encrypt:

Context

StackExchange Code Review Q#104051, answer score: 5

Revisions (0)

No revisions yet.