Enigma Machine Python Project
An enigma machine in Python that will take text input in the console or from a file and output to console, file, or email! Interchangeable wheels and salted cyphertext! The user can define the number of character for salt and email password can be stored encryped!
And a simpler verion in Javascript. https://hull1.com/enigma
# -*- coding: utf-8 -*-
"""
Created on Thu Apr 30 16:39:02 2020
Enigma Machine
@author: HULLB
"""
import random
import re
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
############################ USER INPUT #######################################
userWheel = input('Select 3 of 5 Rotars [123]: ')
if userWheel:
while len(userWheel) != 3 or re.search('[a-zA-Z]', userWheel) != None:
print('Invalid Input')
userWheel = input('Select 3 of 5 Rotars [123]: ')
userPos = input('Select 3 Rotar Positions [ABC]: ')
if userPos:
while len(userPos) != 3 or re.search('[0-9]', userPos) != None:
print('Invalid Input')
userPos = input('Select 3 Rotar Positions [ABC]: ')
userSalt = input('Select Salt Level [8]: ')
if userSalt:
while re.search('[a-zA-Z]', userSalt) != None or int(userSalt) < 0:
print('Invalid Input')
userSalt = input('Select Salt Level [8]: ')
readFile = 'n'
userMessage = input('Enter plantext message: ')
if userMessage == 'file':
readFile = 'y'
fileName = input('Enter plantext file [text.txt]: ')
sendEmail = 'n'
sendEmail = input('Email cyphertext message? [n]: ')
if sendEmail == 'y':
emailAddress = input('Send To Address? : ')
############################ USER INPUT #######################################
############################ ENTER TEXT #######################################
#Default messages can be enabled for debug
#message = 'abcdefghijklmnopqrstuvwxyz'
#message = 'Call me Fishmeal.'
############################ ENTER TEXT #######################################
############################ DEFAULT SETTINGS #################################
wheelPos1 = 'a'
wheelPos2 = 'b'
wheelPos3 = 'c'
saltValue = 8
############################ DEFAULT SETTINGS #################################
############################ SET VARIABLES ####################################
keyboard = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '.', '!', '?', ',', ':', ';']
avalibleWheel1 = ['k', 'd', 'o', 'g', 'w', 'e', 'q', 'z', 'v', 'u', 'h', 'r', 'n', 'p', 'y', 'c', 'a', 't', 'j', 'b', 'i', 'm', 'x', 's', 'l', 'f', 'K', 'H', 'C', 'N', 'A', 'S', 'L', 'X', 'W', 'V', 'O', 'E', 'M', 'P', 'R', 'G', 'F', 'J', 'B', 'Y', 'I', 'T', 'D', 'U', 'Z', 'Q', '8', '2', '0', '7', '3', '5', '9', '1', '6', '4', ' ', '.', '!', '?', ',', ':', ';']
avalibleWheel2 = ['k', 'g', 'u', 'p', 'e', 'w', 'a', 'f', 'd', 'b', 'v', 'q', 'c', 'l', 'j', 'z', 'h', 'n', 'm', 'o', 'i', 'y', 's', 't', 'r', 'x', 'W', 'P', 'Z', 'K', 'G', 'Q', 'I', 'E', 'M', 'T', 'R', 'V', 'X', 'N', 'U', 'H', 'S', 'L', 'J', 'A', 'Y', 'C', 'B', 'D', 'F', 'O', '7', '9', '6', '8', '0', '5', '2', '1', '3', '4', ' ', '.', '!', '?', ',', ':', ';']
avalibleWheel3 = ['t', 'e', 'c', 'w', 'g', 'x', 'n', 'a', 'l', 'r', 'y', 'h', 'z', 'f', 'u', 'v', 'b', 'o', 'm', 'q', 's', 'j', 'k', 'd', 'i', 'p', 'R', 'X', 'C', 'V', 'A', 'Q', 'T', 'N', 'L', 'F', 'K', 'E', 'D', 'W', 'H', 'S', 'Z', 'G', 'Y', 'J', 'B', 'O', 'P', 'U', 'I', 'M', '9', '2', '5', '8', '3', '0', '6', '4', '7', '1', ' ', '.', '!', '?', ',', ':', ';']
avalibleWheel4 = ['j', 'n', 'u', 'v', 'e', 'g', 'a', 'p', 'm', 'y', 's', 'f', 'i', 'd', 'h', 'o', 'b', 'w', 'x', 'q', 't', 'r', 'l', 'z', 'k', 'c', 'L', 'I', 'Y', 'S', 'A', 'R', 'D', 'P', 'F', 'Z', 'H', 'K', 'W', 'V', 'U', 'Q', 'N', 'O', 'J', 'B', 'X', 'M', 'C', 'T', 'G', 'E', '9', '0', '6', '1', '4', '2', '5', '8', '3', '7', ' ', '.', '!', '?', ',', ':', ';']
avalibleWheel5 = ['p', 'h', 'u', 'f', 'y', 'n', 'r', 'a', 'v', 'c', 'z', 'm', 'q', 'o', 'w', 'l', 'x', 'g', 'j', 'b', 'i', 'k', 't', 'e', 'd', 's', 'N', 'U', 'I', 'T', 'A', 'F', 'G', 'B', 'Z', 'X', 'D', 'Q', 'R', 'J', 'V', 'O', 'L', 'W', 'M', 'C', 'K', 'P', 'H', 'Y', 'S', 'E', '6', '0', '3', '5', '8', '2', '1', '4', '7', '9', ' ', '.', '!', '?', ',', ':', ';']
counter = 10
############################ SET VARIABLES ####################################
############################ DEFAULT SETTINGS #################################
wheel1 = avalibleWheel1
wheel2 = avalibleWheel2
wheel3 = avalibleWheel3
############################ DEFAULT SETTINGS #################################
######################### SET VARIABLES FROM INPUT ############################
if userWheel:
if userWheel[0] == '1':
wheel1 = avalibleWheel1
elif userWheel[0] == '2':
wheel1 = avalibleWheel2
elif userWheel[0] == '3':
wheel1 = avalibleWheel3
elif userWheel[0] == '4':
wheel1 = avalibleWheel4
elif userWheel[0] == '5':
wheel1 = avalibleWheel5
if userWheel[1] == '1':
wheel2 = avalibleWheel1
elif userWheel[1] == '2':
wheel2 = avalibleWheel2
elif userWheel[1] == '3':
wheel2 = avalibleWheel3
elif userWheel[1] == '4':
wheel2 = avalibleWheel4
elif userWheel[1] == '5':
wheel2 = avalibleWheel5
if userWheel[2] == '1':
wheel3 = avalibleWheel1
elif userWheel[2] == '2':
wheel3 = avalibleWheel2
elif userWheel[2] == '3':
wheel3 = avalibleWheel3
elif userWheel[2] == '4':
wheel3 = avalibleWheel4
elif userWheel[2] == '5':
wheel3 = avalibleWheel5
if userPos:
userPos = userPos.lower()
wheelPos1 = userPos[0]
wheelPos2 = userPos[1]
wheelPos3 = userPos[2]
if userSalt:
saltValue = int(userSalt)
if saltValue % 2 != 0:
saltValue = saltValue +1
if userMessage:
if readFile == 'y':
if not fileName:
fileName = 'text.txt'
f = open(fileName, 'r')
message = f.readlines()
f.close()
message = ''.join(message)
#message = message.lower()
if readFile != 'y':
#userMessage = userMessage.lower()
message = userMessage
######################### SET VARIABLES FROM INPUT ############################
############################ DEFINE FUNCTIONS #################################
def emailer(emailAddress):
MY_ADDRESS = 'ENTER EMAIL ADDRESS'
ENCRYPTED_PASSWORD = 'ENTER ENCYPTED PASSWORD'
SMTP_HOST = 'smtp.gmail.com'
SMTP_PORT = '587'
names = ['you']
emails = [emailAddress]
unsaltedENCRYPTED_PASSWORD = desalinate(8, ENCRYPTED_PASSWORD)
PASSWORD = startDencryt(unsaltedENCRYPTED_PASSWORD, avalibleWheel1, avalibleWheel2, avalibleWheel3, 'a', 'b', 'c')
with open('cyphertext.txt', 'r', encoding='utf-8') as emailMessage:
emailMessageContent = emailMessage.read()
#set up the SMTP server
s = smtplib.SMTP(host=SMTP_HOST, port=SMTP_PORT)
s.starttls()
s.login(MY_ADDRESS, PASSWORD)
# For each contact, send the email:
for name, email in zip(names, emails):
msg = MIMEMultipart() # create a message
# setup the parameters of the message
msg['From']=MY_ADDRESS
msg['To']=email
msg['Subject']="Encrypted Message"
# add in the message body
msg.attach(MIMEText(emailMessageContent, 'plain'))
# send the message via the server set up earlier.
s.send_message(msg)
del msg
PASSWORD = ENCRYPTED_PASSWORD
del PASSWORD
# Terminate the SMTP session and close the connection
s.quit()
def encrypt(message, wheel, wheelPosition):
#Set wheel position
wheelStart = wheel.index(wheelPosition)
for turns in range (wheelStart):
wheel.append(wheel.pop(0))
#Split str to array
#message = message.lower()
messageArray = [char for char in message]
#Change character array to number array
messageArrayNum = []
for i in range (len(messageArray)):
messageArrayNum.append(keyboard.index(messageArray[i]))
#Change messageArrayNum based on wheel
cypherMessage = []
for i in range (len(messageArrayNum)):
cypherMessage.append(wheel[messageArrayNum[i]])
#turn the wheel!
wheel.append(wheel.pop(0))
#Reassemble array to str
cypherMessage = ''.join(cypherMessage)
return(cypherMessage)
def decrypt(cypherMessage, wheel, wheelPosition):
#Set wheel position
wheelStart = wheel.index(wheelPosition)
for turns in range (wheelStart):
wheel.append(wheel.pop(0))
#Split str to array
#cypherMessage = cypherMessage.lower()
cypherMessageArray = [char for char in cypherMessage]
#Change character array to number array
cypherMessageArrayNum = []
for i in range (len(cypherMessageArray)):
cypherMessageArrayNum.append(wheel.index(cypherMessageArray[i]))
#turn the wheel!
wheel.append(wheel.pop(0))
#Change messageArrayNum based on wheel
message = []
for i in range (len(cypherMessageArrayNum)):
message.append(keyboard[cypherMessageArrayNum[i]])
#Reassemble array to str
message = ''.join(message)
return(message)
def easySalt(_saltValue, text):
if _saltValue == 0:
saltedtext = text
return(saltedtext)
else:
if _saltValue % 2 != 0:
_saltValue = _saltValue + 1
salt=[]
for i in range(_saltValue):
salt.append(keyboard[random.randint(0, len(keyboard)-1)])
salt = ''.join(salt)
saltedtext = salt + text
salt=[]
for i in range(_saltValue):
salt.append(keyboard[random.randint(0, len(keyboard)-1)])
salt = ''.join(salt)
saltedtext = saltedtext + salt
return(saltedtext)
def easyUnsalt(saltyValue, text):
if saltyValue == 0:
saltedtext = text
return(saltedtext)
else:
unsaltedtext = text[saltyValue:]
unsaltedtext = unsaltedtext[:-saltyValue]
return(unsaltedtext)
def salt(_saltValue, text):
if _saltValue == 0:
saltedtext = text
return(saltedtext)
else:
if _saltValue % 2 != 0:
_saltValue = _saltValue + 1
salt = []
if len(text) % 2 != 0:
text = text + ' '
modtext = True
else:
modtext = False
for i in range (_saltValue):
salt.append(keyboard[random.randint(0, len(keyboard)-1)])
salt = ''.join(salt)
saltPlace = len(text) // 2
saltedText = text[:saltPlace] + salt + text[saltPlace:]
if modtext == True:
saltedText = saltedText[:-1]
return saltedText
def unsalt(_saltValue, saltedText):
if _saltValue == 0:
unsaltedtext = saltedText
return(unsaltedtext)
else:
if _saltValue % 2 != 0:
_saltValue = _saltValue + 1
saltPlace = len(saltedText) // 2
saltHalf = _saltValue //2
unsaltedText1 = saltedText[:-saltPlace - saltHalf]
unsaltedText2 = saltedText[saltHalf - saltPlace:]
return unsaltedText1 + unsaltedText2
def startEncryt(message, wheel_1, wheel_2, wheel_3, wheelPos_1, wheelPos_2, wheelPos_3):
arrayOfCypherMessages = []
arrayOfCypherMessages.append(message)
for i in range (0, counter):
if i % 2 == 0:
arrayOfCypherMessages.append(encrypt(arrayOfCypherMessages[i], wheel_1, wheelPos_1))
elif i % 3 == 0:
arrayOfCypherMessages.append(encrypt(arrayOfCypherMessages[i], wheel_2, wheelPos_2))
else:
arrayOfCypherMessages.append(encrypt(arrayOfCypherMessages[i], wheel_3, wheelPos_3))
saltedCypherMessage = easySalt(saltValue, arrayOfCypherMessages[counter])
saltedCypherMessage = salt(saltValue, saltedCypherMessage)
return(saltedCypherMessage)
def desalinate(salt_Value, saltedCypherMessage):
unsaltedCypherMessage = easyUnsalt(salt_Value, saltedCypherMessage)
unsaltedCypherMessage = unsalt(salt_Value, unsaltedCypherMessage)
return unsaltedCypherMessage
def startDencryt(unsaltedCypherMessage, wheel_1, wheel_2, wheel_3, wheelPos_1, wheelPos_2, wheelPos_3):
arrayOfDecypheredMessages = []
arrayOfDecypheredMessages.append(unsaltedCypherMessage)
for i in range (0, counter):
if i % 2 != 0:
arrayOfDecypheredMessages.append(decrypt(arrayOfDecypheredMessages[i], wheel_1, wheelPos_1))
elif i % 3 == 0:
arrayOfDecypheredMessages.append(decrypt(arrayOfDecypheredMessages[i], wheel_2, wheelPos_2))
else:
arrayOfDecypheredMessages.append(decrypt(arrayOfDecypheredMessages[i], wheel_3, wheelPos_3))
return arrayOfDecypheredMessages[counter]
############################ DEFINE FUNCTIONS #################################
############################### START #########################################
print(message)
print('\n')
saltedCypherMessage = startEncryt(message, wheel1, wheel2, wheel3, wheelPos1, wheelPos2, wheelPos3)
print(saltedCypherMessage)
"""
#Uncomment to test decrypt without running seperate script
print('\n')
unsaltedCypherMessage = desalinate(saltValue, saltedCypherMessage)
print(unsaltedCypherMessage)
print('\n')
decypheredMessage = startDencryt(unsaltedCypherMessage, wheel1, wheel2, wheel3, wheelPos1, wheelPos2, wheelPos3)
print(decypheredMessage)
"""
print('\n')
print ('Output written to cyphertext.txt')
print(saltedCypherMessage, file=open('cyphertext.txt', 'w'))
if sendEmail == 'y':
emailer(emailAddress)
print('\n')
print ('Output send to ', emailAddress)
################################ END ##########################################
As it turns out it’s also useful the have some Python code that will generate new wheels for the enigma machine.
# -*- coding: utf-8 -*-
"""
Created on Sun May 3 15:29:44 2020
Enigma Machine Wheel Generator
@author: HULLB
"""
import random
def wheelGen():
wheel = []
keyboard = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '.', '!', '?', ',', ';', ':', '(', ')', '$', '*', '[', ']', '&', '-', '=', '@', '"', '%', '~', '|', '/', '_']
for i in range (0, len(keyboard)):
num = random.randint(0, len(keyboard)-1)
char = keyboard[num]
wheel.append(char)
del keyboard[num]
#wheel.append(' ')
print(wheel)
for i in range(0,5):
wheelGen()
Further Reading:
https://en.wikipedia.org/wiki/Enigma_machine