296 lines
7.5 KiB
Python
296 lines
7.5 KiB
Python
import curses
|
|
import random
|
|
import time
|
|
import os
|
|
from fallout_functions import slowWrite, upperInput, addSound
|
|
|
|
################## text strings ######################
|
|
|
|
HEADER_TEXT = "ROBCO INDUSTRIES (TM) TERMLINK PROTOCOL"
|
|
|
|
################## global "constants" ################
|
|
|
|
# number of characters for hex digits and spaces
|
|
CONST_CHARS = 16
|
|
|
|
# position of the attempt squares
|
|
SQUARE_X = 19
|
|
SQUARE_Y = 3
|
|
|
|
LOGIN_ATTEMPTS = 4
|
|
|
|
HEADER_LINES = 5
|
|
|
|
# amount of time to pause after correct password input
|
|
LOGIN_PAUSE = 3
|
|
|
|
# starting number for hex generation
|
|
START_HEX = 0xF650
|
|
|
|
# list of possible symbols for password hiding
|
|
SYMBOLS = "!@#$%^*()_-+={}[]|\\:;'\",<>./?"
|
|
|
|
################## functions #########################
|
|
|
|
|
|
def generateHex(n):
|
|
"""
|
|
generates n numbers starting at START_HEX and increasing by 12 each time
|
|
"""
|
|
num = START_HEX
|
|
list = []
|
|
for i in range(n):
|
|
list.append(num)
|
|
num += 12
|
|
return list
|
|
|
|
|
|
def getSymbols(n):
|
|
"""
|
|
return n random symbols
|
|
"""
|
|
count = len(SYMBOLS)
|
|
result = ""
|
|
for i in range(int(n)):
|
|
result += SYMBOLS[random.randint(0, count - 1)]
|
|
return result
|
|
|
|
|
|
def getPasswords():
|
|
"""
|
|
Returns an array of strings to be used as the password and the decoys
|
|
"""
|
|
groups = []
|
|
|
|
# script file / password file location
|
|
__location__ = os.path.realpath(
|
|
os.path.join(os.getcwd(), os.path.dirname(__file__))
|
|
)
|
|
|
|
# read from passwords.txt
|
|
with open(os.path.join(__location__, "passwords.txt")) as pwfile:
|
|
for line in pwfile:
|
|
if not line.strip():
|
|
groups.append([])
|
|
elif len(groups) > 0:
|
|
groups[len(groups) - 1].append(line[:-1])
|
|
|
|
passwords = groups[random.randint(0, len(groups) - 1)]
|
|
|
|
random.shuffle(passwords)
|
|
return passwords
|
|
|
|
|
|
def getFiller(length, passwords):
|
|
"""
|
|
Return a string of symbols with potential passwords mixed in
|
|
|
|
length - the length of the string to create
|
|
passwords - an array of passwords to hide in the symbols
|
|
"""
|
|
filler = getSymbols(length)
|
|
|
|
# add the passwords to the symbols
|
|
pwdLen = len(passwords[0])
|
|
pwdCount = len(passwords)
|
|
i = 0
|
|
for pwd in passwords:
|
|
# skip a distance based on total size to cover then place a password
|
|
maxSkip = int(length / pwdCount - pwdLen)
|
|
i += random.randint(maxSkip - 2, maxSkip)
|
|
filler = filler[:i] + pwd + filler[i + pwdLen :]
|
|
i += pwdLen
|
|
return filler
|
|
|
|
|
|
def initScreen(scr):
|
|
"""
|
|
Fill the screen to prepare for password entry
|
|
|
|
scr - curses window returned from curses.initscr()
|
|
"""
|
|
size = scr.getmaxyx()
|
|
height = size[0]
|
|
width = size[1]
|
|
fillerHeight = height - HEADER_LINES
|
|
|
|
hexes = generateHex(fillerHeight * 2)
|
|
|
|
hexCol1 = hexes[:fillerHeight]
|
|
hexCol2 = hexes[fillerHeight:]
|
|
|
|
# generate the symbols and passwords
|
|
fillerLength = width / 2 * fillerHeight
|
|
passwords = getPasswords()
|
|
filler = getFiller(fillerLength, passwords)
|
|
fillerCol1, fillerCol2 = filler[0 : len(filler) // 2], filler[len(filler) // 2 :]
|
|
|
|
# print(fillerCol1)
|
|
# time.sleep(15)
|
|
# print(fillerCol2)
|
|
# time.sleep(15)
|
|
|
|
# each column of symbols and passwords should be 1/4 of the screen
|
|
fillerWidth = int(width / 4)
|
|
|
|
# print the header stuff
|
|
slowWrite(scr, HEADER_TEXT)
|
|
slowWrite(scr, "\nENTER PASSWORD NOW\n\n")
|
|
slowWrite(scr, str(LOGIN_ATTEMPTS) + " ATTEMPT(S) LEFT: ")
|
|
for i in range(LOGIN_ATTEMPTS):
|
|
scr.addch(curses.ACS_BLOCK)
|
|
slowWrite(scr, " ")
|
|
slowWrite(scr, "\n\n")
|
|
|
|
# print the hex and filler
|
|
for i in range(fillerHeight):
|
|
slowWrite(
|
|
scr,
|
|
"0x%X %s"
|
|
% (hexCol1[i], fillerCol1[i * fillerWidth : (i + 1) * fillerWidth]),
|
|
1,
|
|
silent=True,
|
|
)
|
|
if i < fillerHeight - 1:
|
|
scr.addstr("\n")
|
|
|
|
for i in range(fillerHeight):
|
|
scr.move(HEADER_LINES + i, int(CONST_CHARS / 2 + fillerWidth))
|
|
slowWrite(
|
|
scr,
|
|
"0x%X %s"
|
|
% (hexCol2[i], fillerCol2[i * fillerWidth : (i + 1) * fillerWidth]),
|
|
1,
|
|
silent=True,
|
|
)
|
|
|
|
scr.refresh()
|
|
|
|
return passwords
|
|
|
|
|
|
def moveInput(scr, inputPad):
|
|
"""
|
|
moves the input pad to display all text then a blank line then the cursor
|
|
"""
|
|
size = scr.getmaxyx()
|
|
height = size[0]
|
|
width = size[1]
|
|
|
|
inputPad.addstr("\n>")
|
|
|
|
# cursor position relative to inputPad
|
|
cursorPos = inputPad.getyx()
|
|
|
|
inputPad.refresh(
|
|
0,
|
|
0,
|
|
int(height - cursorPos[0] - 1),
|
|
int(width / 2 + CONST_CHARS),
|
|
int(height - 1),
|
|
int(width - 1),
|
|
)
|
|
|
|
|
|
def userInput(scr, passwords):
|
|
"""
|
|
let the user attempt to crack the password
|
|
|
|
scr - curses window returned from curses.initscr()
|
|
passwords - array of passwords hidden in the symbols
|
|
"""
|
|
size = scr.getmaxyx()
|
|
height = size[0]
|
|
width = size[1]
|
|
|
|
# set up a pad for user input
|
|
inputPad = curses.newpad(height, int(width / 2 + CONST_CHARS))
|
|
|
|
attempts = LOGIN_ATTEMPTS
|
|
|
|
# randomly pick a password from the list
|
|
pwd = passwords[random.randint(0, len(passwords) - 1)]
|
|
curses.noecho()
|
|
|
|
while attempts > 0:
|
|
# move the curser to the correct spot for typing
|
|
scr.move(int(height - 1), int(width / 2 + CONST_CHARS + 1))
|
|
|
|
# scroll user input up as the user tries passwords
|
|
moveInput(scr, inputPad)
|
|
|
|
guess = upperInput(scr, False, False)
|
|
cursorPos = inputPad.getyx()
|
|
|
|
# write under the last line of text
|
|
inputPad.move(cursorPos[0] - 1, cursorPos[1] - 1)
|
|
inputPad.addstr(">" + guess.upper() + "\n")
|
|
|
|
# user got password right
|
|
if guess.upper() == pwd.upper():
|
|
|
|
addSound("correctpass")
|
|
inputPad.addstr(">Exact match!\n")
|
|
inputPad.addstr(">Please wait\n")
|
|
inputPad.addstr(">while system\n")
|
|
inputPad.addstr(">is accessed.\n")
|
|
|
|
moveInput(scr, inputPad)
|
|
|
|
time.sleep(LOGIN_PAUSE)
|
|
return pwd
|
|
|
|
# wrong password
|
|
else:
|
|
pwdLen = len(pwd)
|
|
matched = 0
|
|
try:
|
|
for i in range(pwdLen):
|
|
if pwd[i].upper() == guess[i].upper():
|
|
matched += 1
|
|
except IndexError:
|
|
pass # user did not enter enough letters
|
|
|
|
addSound("wrongpass")
|
|
inputPad.addstr(">Entry denied\n")
|
|
inputPad.addstr(">" + str(matched) + "/" + str(pwdLen) + " correct.\n")
|
|
|
|
attempts -= 1
|
|
# show remaining attempts
|
|
scr.move(SQUARE_Y, 0)
|
|
scr.addstr(str(attempts))
|
|
scr.move(SQUARE_Y, SQUARE_X)
|
|
for i in range(LOGIN_ATTEMPTS):
|
|
if i < attempts:
|
|
scr.addch(curses.ACS_BLOCK)
|
|
else:
|
|
scr.addstr(" ")
|
|
scr.addstr(" ")
|
|
|
|
# Out of attempts
|
|
return None
|
|
|
|
|
|
def runLogin(scr):
|
|
"""
|
|
Start the login portion of the terminal
|
|
Returns the password if the user correctly guesses it
|
|
"""
|
|
curses.use_default_colors()
|
|
size = scr.getmaxyx()
|
|
width = size[1]
|
|
height = size[0]
|
|
random.seed()
|
|
# set screen to initial position
|
|
scr.erase()
|
|
scr.move(0, 0)
|
|
passwords = initScreen(scr)
|
|
return userInput(scr, passwords)
|
|
|
|
|
|
def beginLogin():
|
|
"""
|
|
Initialize curses and start the login process
|
|
Returns the password if the user correctly guesses it
|
|
"""
|
|
return curses.wrapper(runLogin)
|