This is the first piece of code that I write that's not just an exercise but a full program I can actually run.
Features:
- Generate random letter/number pairs for each "printable" ASCII character
- Save those pairs in a pickle file
- Encode/decode any string based on that
- Basic user interface
I would greatly appreciate any advice, feedback, comment you can give me on it, as I've been learning programming by myself for a couple months now. Did I structure it right, is it how a program is supposed to be put together, is it commented well and enough?
Also, I realize it's probably a weird way of encrypting anything and there's surely a better/safer/easier way to do encryption but it was mostly an excuse to write the program.
from string import printable,\
ascii_letters,\
ascii_lowercase,\
ascii_uppercase
import random
import pickle
def generate_code():
# Generates a random number for each printable ASCII character,
# Returns a list of (character, number) tuples for each pair
characters = printable
numbers = random.sample(range(len(characters) + 1000), len(characters))
code = list(zip(characters, numbers))
return code
def encode(string, code):
# Replaces each character of string with its code-number
# and adds a random number of random letters
coded_string = []
# find matching number for each character
for character in string:
for letter, number in code:
if character == letter:
# add the matching number
coded_string.append(str(number))
coded_string.append(''.join(
random.sample(
ascii_lowercase,
random.randint(2,6)
)
)
)# random letters used to separate numbers
for _ in range(random.randrange(len(string))):
coded_string.insert(
random.randrange(len(coded_string)),
''.join(random.sample(
ascii_uppercase, random.randint(1, 3)
))
) # random uppercase letters randomly inserted
return ''.join(coded_string)
def decode(string, code):
def retrieve_letter(n):
for letter, number in code:
if int(n) == number:
return letter
else:
continue
return "No Match found"
decoded_list = []
decoded_string = ''
character = ''
for item in string:
if item.isdigit():
character += item
else:
if character != '':
decoded_list.append(character)
character = ''
for n in decoded_list:
decoded_string += retrieve_letter(n)
return decoded_string
def save_code(object):
with open("code.p", "wb") as f:
pickle.dump(object, f)
def load_code():
try:
with open("code.p", "rb") as f:
return pickle.load(f)
except FileNotFoundError:
print("No saved code found.")
return None
def main():
import time
code = generate_code()
print("Welcome to my encryption program!")
while True: #Code selection menu
print("Please select an option:")
print("1: Use saved code")
print("2: Use new code and overwrite saved code")
print("3: Use new code and keep saved code")
prompt = input(">")
if prompt == "1":
if load_code() == None:
code = generate_code()
else:
code = load_code()
break
elif prompt == "2":
save_code(code)
break
elif prompt == "3":
break
else:
"This option is not available"
continue
while True: #Main Loop, asks user if he wants to encode/decode
print("Would you like to encrypt a phrase?(Y/N)")
prompt = input(">")
if prompt in ("N", "no", "No", "n"):
print("Press Enter to exit or type in anything to continue:")
prompt = input(">")
if prompt == '':
print ("Thank you for using the program, good bye!")
time.sleep(2)
break
else:
phrase = input("Enter your text here :\n>")
print (f"\nHere is your code : {encode(phrase, code)}\n")
print("Would you like to decrypt a phrase?(Y/N)")
prompt = input(">")
if prompt in ("N", "no", "No", "n"):
print("Press Enter to exit or type in anything to continue:")
prompt = input(">")
if prompt == '':
print ("Thank you for using the program, good bye!")
time.sleep(2)
break
else:
coded_phrase = input("Enter your code here :\n>")
print(f"\nHere is your original text: {decode(coded_phrase, code)}\n")
time.sleep(1)
input("Press Enter to continue")
print("\n")
if __name__ == '__main__':
main()
Addendum : To help anyone who, like me, knows very little about encryption, I found this video from the YouTube channel Computerphile.
Here Dr Mike Pound talks about the Secure Hashing Algorithm (SHA1, which however is not a safe encryption algorithm anymore), explaining how it works to create a hash, a seemingly randomly generated string of a fixed length from an input.
1 Answer 1
Congrats on teaching yourself programming! You seem to be doing a good job of it. Aside from the fact that I would not want to use a homebrew crypto algorithm the following should be taken as minimal requirements for being able to rely on this code for productive work:
- The
sleep
calls do not add anything useful.sleep
should be avoided like the plague in production code. - I would expect this sort of program to follow some general *nix shell script guidelines:
- Read from standard input and
print()
reusable data to standard output. So rather thanopen("code.p", ...)
andinput()
you'd read the input stream, perform decryption or encryption based on parameters, andprint()
the reusable data before exiting. In other words, after running something like./my.py --encrypt --key=password1 < input.txt | ./my.py --decrypt --key=password1 > output.txt
(I don't know what that would look like on Windows, sorry) input.txt and output.txt should be identical. - Expose all functionality in a non-interactive manner.
- Read from standard input and
- I can't follow the algorithm. I see a lot of mixing of numbers and letters, and what looks like an attempt to obfuscate the ciphertext by adding random length strings to the contents. Some unit tests (or even doctests) would be very useful to understand how this is meant to be read and understood.
- It's hard to follow two
while True
loops. The first one doesn't even seem to be necessary - it only loops back if the user specifies an invalid option. It would be perfectly valid in this case to exit the program with an error instead. Running the code through at least one linter such as
flake8
orpycodestyle
until it passes both without any warnings will help you create more idiomatic Python. Since you're using Python 3 I would also recommend adding type hints and running through a strictmypy
configuration such as this one:[mypy] check_untyped_defs = true disallow_untyped_defs = true ignore_missing_imports = true no_implicit_optional = true warn_redundant_casts = true warn_return_any = true warn_unused_ignores = true
-
\$\begingroup\$ When you say "requirements for putting this code into production" you mean "make it usable by professionals"? Also regarding the while true loops, I wanted to prompt the user again for the same choice if his choice is invalid, how should I do it? And loads of things I've never heard of but thanks a lot, that means I know what to research now! \$\endgroup\$Clepsyd– Clepsyd2018年12月07日 02:15:53 +00:00Commented Dec 7, 2018 at 2:15
-
\$\begingroup\$ I'd consider those kind of the same thing - I think I can use "production" to mean it's performing useful work and can be built on top of, not necessarily that it's on a server answering queries. \$\endgroup\$l0b0– l0b02018年12月07日 02:16:37 +00:00Commented Dec 7, 2018 at 2:16
-
\$\begingroup\$ Also : "read from standard input, write to standard output" ? I don't really understand that one. Also I'm on Windows at the moment. \$\endgroup\$Clepsyd– Clepsyd2018年12月07日 02:20:45 +00:00Commented Dec 7, 2018 at 2:20