#!/usr/bin/env python3 # -*-mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- # https://github.com/reid-k/gridfire from __future__ import unicode_literals, print_function __program__ = "Gridfire" __version__ = "v0.5+testforge" __doc__ = """ Gridfire is inspired by gpggrid, a security tool included in Tinfoil Hat Linux, intended to resist shoulder-surfing and keylogging. For more information on the project, see the README file distributed with Gridfire. Gridfire is named after a fictional superweapon in Iain M Banks' Culture novels. See http://everything2.com/title/Gridfire for more. RIP Iain M Banks (1954-02-16 to 2013-06-09) usage: gridfire.py [-h] [-a TTYALERT] [-c] [-g] [-u] [-l] [-b] [-o] [-d] [-f] [-p] [-w] [-m METHOD] [-n] [-v VERBOSITY] [-S SINGLE] [-D DOUBLE] [-R REPEAT ] [ -P POS] [-H HEADER] [-A ANSWER] [-O fd] [STDIN COMMAND [STDIN ARGS ...]] positional arguments: STDIN COMMAND Arguments to run a subprocess wih the password in stdin. optional arguments: -h, --help show this help message and exit -a TTYALERT, --ttyalert TTYALERT Set the alert mode (none, beep or flash) -c Force concealment of entered text. -g, --grid Ignore other options and behave like gpggrid. -u Allow uppercase letters [A-Z]. -l Allow lowercase letters [a-z]. -b Allow binary numbers [0-1]. -o Allow octal numbers [0-7]. -d Allow decimal numbers [0-9]. -f Allow hexadecimal numbers [0-9][a-f][A-F]. -p Allow punctuation. -w Allow whitespace. -m METHOD, --method METHOD Specify indexing method (0-3, higher is more secure, default=3) -n Append N newlines on output. -v VERBOSITY, --verbosity VERBOSITY Specify verbosity (0-5, higher is verbose, default=3) -S SINGLE, --single SINGLE Arg to pass to a subprocess with -SINGLE=password. -D DOUBLE, --double DOUBLE Arg to pass to a subprocess with --DOUBLE=password. -R, --repeat Repeat the password with a newline to a subprocess. -P POS, --pos POS Position to place the Arg to a subprocess with --DOUBLE/SINGLE=password. -H HEADER, --header HEADER Header line as a prompt (default ""). -A ANSWER, --answer ANSWER Skip the grid and give the answer (for testing only). """ #Learn more about Tinfoil Hat Linux: #Homepage: http://tinfoilhat.shmoo.com/ #Wikipedia: https://en.wikipedia.org/wiki/Tinfoil_Hat_Linux # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . #TODO: Decide whether or not implement method 4 #TODO: Fix row index for non-default charsets import os import sys import signal import curses import locale import string import random import argparse import _curses from io import StringIO import subprocess from time import sleep PY3 = sys.version_info[0] == 3 VERBOSE = 3 # # 0: stdin # 1: stdout # 2: stderr iOUT_FD = sys.stdout try: from pinentry import PinEntry except ImportError: PinEntry = None oPinEntry = None try: from dialog import Dialog except ImportError: Dialog = None oDIALOG = None oLAST_DIR = '/tmp/' # Allow stderr for the caller to use def debug(message): if VERBOSE <= 4: return sys.stdout.write('DEBUG: ' + repr(message) +'\n') pass def info(message): if VERBOSE <= 3: return sys.stdout.write('INFO: ' + repr(message) +'\n') pass def warn(message): if VERBOSE <= 2: return sys.stdout.write('WARN: ' + repr(message) +'\n') pass def error(message): if VERBOSE <= 1: return sys.stdout.write('ERROR: ' + message +'\n') pass class DummyContextManager: def __enter__(self): return self def __exit__(self, *exc): return False #Helps curses play nice locale.setlocale(locale.LC_ALL, '') code = locale.getpreferredencoding() ##### Defaults & Initial variables ##### charsets = '' min_x, min_y = 55, 17 upperchar = '' lowerchar = '' out = '' hide_out = 0 newline = 1 def spaceout(s): i=len(s)-1 #number of spaces to be inserted while i >= 1: s = s[:-i]+" "+s[-i:] i -= 1 return s def make_parser(): """Argument parsing""" global VERBOSE parser = argparse.ArgumentParser(add_help=True) parser.add_argument('-a', '--ttyalert', type=str, help='Set the alert mode (none, beep or flash)') parser.add_argument('-c', help="Force concealment of entered text.", action="store_true") parser.add_argument('-g', '--grid', help="Ignore other options and behave like gpggrid.", action="store_true") #~ parser.add_argument('-u', dest='char_u', action='store_true') parser.add_argument('-u', help="Allow uppercase letters [A-Z].", action="store_true") parser.add_argument('-l', help="Allow lowercase letters [a-z].", action="store_true") parser.add_argument('-b', help="Allow binary numbers [0-1].", action="store_true") parser.add_argument('-o', help="Allow octal numbers [0-7].", action="store_true") parser.add_argument('-d', help="Allow decimal numbers [0-9].", action="store_true") parser.add_argument('-f', help="Allow hexadecimal numbers [0-9][a-f][A-F].", action="store_true") parser.add_argument('-p', help="Allow punctuation.", action="store_true") parser.add_argument('-w', help="Allow whitespace.", action="store_true") parser.add_argument('-m', '--method', help='Specify indexing method (0-3, higher is more secure, default=3)', dest='method', type=int, default=3) parser.add_argument('-n', help="Append newlines on output.", dest='newline', type=int, default=1) parser.add_argument('-v', '--verbosity', help='Specify verbosity (0-5, higher is verbose, default=3)', type=int, default=3) parser.add_argument('-S', '--single', help="Arg to pass to a subprocess with -SINGLE=password.", dest="single", default='') parser.add_argument('-D', '--double', help="Arg to pass to a subprocess with --DOUBLE=password.", dest="double", default='') parser.add_argument('-R', '--repeat', help="Repeat the password with a newline to a subprocess", action="store_true") parser.add_argument('-P', '--pos', help="Position to place the Arg to a subprocess with --DOUBLE/SINGLE=password.", dest="pos", type=int, default=1) parser.add_argument('-H', '--header', help="Header line as a prompt (default \"\").", dest="header", default='') parser.add_argument('-A', '--answer', help="Skip the grid and give the answer (for testing only).", dest="answer", default='') parser.add_argument('-O', '--output_fd', help="Output file descriptor to print the answer (default 1).", dest="output_fd", type=int, default=1) parser.add_argument('sargs', help="Arguments to run a subprocess wih the password in stdin.", type=str, default='', nargs='*') # metavar='STDIN COMMAND', return parser def make_grid(args): """Define character sets""" punct = spaceout(string.punctuation) punct2 = punct[52:] punct = punct[:52] whitespace = spaceout(string.whitespace) #Select character sets #TODO: Can this be simplified? args['u'+'l'] or whatnot? #use_uppercases = (-1 != charsets.find("u")) use_uppercases = args['u'] use_lowercases = args['l'] use_bindigits = args['b'] use_octdigits = args['o'] use_decdigits = args['d'] use_hexdigits = args['f'] use_punct = args['p'] use_whitespace = args['w'] _setcount = args['u']+args['l']+args['b']+args['o']+args['d']+args['f']+args['p']+args['w'] #if use_uppercases+use_lowercases+use_bindigits+use_octdigits+use_decdigits+use_hexdigits+use_punct+use_whitespace == 0: if _setcount == 0: use_uppercases = 1 use_lowercases = 1 use_decdigits = 1 use_punct = 1 use_whitespace = 1 use_numbers = 0 numbers = '' if use_bindigits+use_octdigits+use_decdigits+use_hexdigits: use_numbers = 1 if use_bindigits: numbers = '0 1' if use_octdigits: numbers = spaceout(string.octdigits) if use_decdigits: numbers = spaceout(string.digits) if use_hexdigits: numbers = spaceout(string.hexdigits) ##### Build static grid ##### grid = [] if use_uppercases: if PY3: grid.append(spaceout(string.ascii_uppercase)) else: grid.append(spaceout(string.uppercase)) if use_lowercases: if PY3: grid.append(spaceout(string.ascii_lowercase)) else: grid.append(spaceout(string.lowercase)) if use_numbers: grid.append(numbers) if use_punct: grid.append(punct) grid.append(punct2) return (use_uppercases, use_lowercases, use_decdigits, use_punct, use_whitespace, punct, punct2, whitespace, numbers, grid, ) ##### Build user interface ##### usagebanner = "Choose from grid by typing letter pairs like eF or Dg" #? not hasattr(sys, 'frozen') and if Dialog is not None and os.path.isfile('/usr/bin/dialog'): shortcuts = "4:Shell 5:FName 6:FContents 7:Delete 8:Show/Hide 9:Quit 0:Done" else: shortcuts = "7:Delete 8:Show/Hide 9:Quit 0:Done" def total_rows(use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct): return use_lowercases+use_uppercases+use_decdigits+use_whitespace+use_punct+use_punct+1 def iMain(lSysArgv): global password, exit_code, VERBOSE, iOUT_FD, oDIALOG, oLAST_DIR global min_x, min_y, upperchar, lowerchar, out, hide_out global oLAST_DIR padpos = 0 stdscr = None password = '' exit_code = -1 out = '' upperchar = '' lowerchar = '' if PY3: uppercase = string.ascii_uppercase lowercase = string.ascii_lowercase else: uppercase = string.uppercase lowercase = string.lowercase # Check for required size before continuing def check_size(stdscr): (size_y,size_x) = stdscr.getmaxyx() if ((size_x < min_x)|(size_y < min_y)): error( __program__+" needs a "+str(min_x)+"x"+str(min_y)+" character area to function.\n") error( "please adjust your terminal size and restart "+__program__+".") sys.exit(1) def pad_draw(stdscr, pad, ly, lx, hy, hx, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, title): rv = __program__ +" "+ __version__ pad.addstr(0,0," "*(hx+1),curses.A_REVERSE) pad.addstr(0,2,rv,curses.A_REVERSE) pad.addstr(1,0," "*min_x) #~ pad.addstr(0,40," ") pad.addstr(0,40,lowerchar,curses.A_REVERSE) pad.addstr(0,41,upperchar,curses.A_REVERSE) pad.addstr(0,73,"SHOW") if hide_out: pad.addstr(0,73,"HIDE") pad.addstr(1,0,header) pad.addstr(2,0,">>") pad.addstr(2,4," "*(len(out)+1)) disp_out = out if hide_out: disp_out = len(out)*"*" pad.addstr(2,4,disp_out) ##Draw the grid global gridrow gridrow = 6 gridoffset = 4 #Grid Indexes pad.addstr(gridrow-2,gridoffset,spaceout(col_index)) for t in range(0, total_rows(use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct)): pad.addstr(gridrow+t,1,row_index[t]) t += 1 #Index selection highlighting if len(upperchar): stdscr.addstr(gridrow-2,4+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE) if len(lowerchar): stdscr.addstr(gridrow+row_index.find(lowerchar),1,lowerchar,curses.A_REVERSE) #Static grid elements ### New grid draw method ### r = 0 for s in grid: pad.addstr(gridrow,gridoffset,grid[r]) r += 1 gridrow += 1 if use_whitespace: #Draw the whitespace row pad.addstr(gridrow,gridoffset," :Space :Tab :Enter") pad.addstr(gridrow,gridoffset,col_index[0]) pad.addstr(gridrow,9+gridoffset,col_index[1]) pad.addstr(gridrow,18+gridoffset,col_index[2]) #If the corresponding columns are selected, highlight them if len(upperchar)&(upperchar==col_index[0]): stdscr.addstr(gridrow,4+col_index.find(upperchar),upperchar,curses.A_REVERSE) if len(upperchar)&(upperchar==col_index[1]): stdscr.addstr(gridrow,11+col_index.find(upperchar),upperchar,curses.A_REVERSE) if len(upperchar)&(upperchar==col_index[2]): stdscr.addstr(gridrow,19+col_index.find(upperchar),upperchar,curses.A_REVERSE) gridrow += 1 #Draw the 'done' line, and highlight column if selected pad.addstr(gridrow,gridoffset," :Ignore") pad.addstr(gridrow,gridoffset,col_index[0]) # pad.addstr(gridrow,9+gridoffset,col_index[1]) # pad.addstr(gridrow,18+gridoffset,col_index[2]) if len(upperchar)&(upperchar==col_index[0]): stdscr.addstr(gridrow,4+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE) if len(upperchar)&(upperchar==col_index[1]): stdscr.addstr(gridrow,10+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE) if len(upperchar)&(upperchar==col_index[2]): stdscr.addstr(gridrow,17+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE) #Help banners pad.addstr(14,0,title) pad.addstr(15,0,usagebanner) # (1+hx-len(shortcuts))*' '+ pad.addstr(16,0,shortcuts,curses.A_REVERSE) pad.refresh(padpos, 0, ly, lx, hy, hx) def main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, title): (max_y, max_x,) = stdscr.getmaxyx() pad_draw(stdscr, pad, 0, 0, (max_y-2), (max_x-1), use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, title) stdscr.move(max_y-1,0) try: curses.curs_set(0) except: pass stdscr.refresh() def cycle_index(cyclemethod=3): #Argument specifies method, default is 3 global col_index, row_index if PY3: uppercase = string.ascii_uppercase lowercase = string.ascii_lowercase else: uppercase = string.uppercase lowercase = string.lowercase #Method 0: Ordered letters and numbers, no randomization #This method has 1 possible state and provides no security. #Included to allow gridfire's use to limit possible symbols entered. #For example, hex digits only when entering a WEP key. if cyclemethod==0: col_index = uppercase row_index = lowercase #Method 1: Ordered letters, random offset (as with gpggrid) #The math might differ from gpggrid #This method has 676 or 26^2 possible states if cyclemethod==1: offset = random.randint(0,25) col_index = uppercase[offset:]+uppercase[:offset] offset = random.randint(0,25) row_index = lowercase[offset:]+lowercase[:offset] #Method 2: use random.shuffle() to shuffle one index in place #This might not work very well, see module documentation on #issues with small len(x) exceeding period of RNGs # http://docs.python.org/2/library/random.html ##This method has: ##403,291,461,126,605,635,584,000,000 ##or 26! possible states (403.3 septillion) if cyclemethod==2: col_index = uppercase colcycle = list(uppercase) random.shuffle(colcycle) col_index = ''.join(colcycle) row_index = col_index.lower() #Method 3: use random.shuffle() to shuffle indices in place #This might not work very well, see module documentation on #issues with small len(x) exceeding period of RNGs # http://docs.python.org/2/library/random.html #This method has: #162,644,002,617,632,464,507,038,883,409,628,607,021,056,000,000,000,000 ##or 26!^2 possible states (162.6 sexdecillion). if cyclemethod==3: col_index = uppercase colcycle = list(uppercase) random.shuffle(colcycle) col_index = ''.join(colcycle) row_index = lowercase rowcycle = list(lowercase) random.shuffle(rowcycle) row_index = ''.join(rowcycle) #TODO: Implement method 4 (mixed cases in row/column indexes) # (This would require redoing input handling to recognize same-case pairs) #Method 4 would have: #80,658,175,170,943,878,571,660,636,856,403,766,975,289,505,440,883,277,824,000,000,000,000 #or 52! possible states (80.6 Unvigintillion). def quit_scr(leave_message = "", exit_status= 0): global password, exit_code stdscr.keypad(0) curses.echo() curses.nocbreak() curses.endwin() # print leave_message password = leave_message exit_code = exit_status parser = make_parser() args = vars(parser.parse_args(lSysArgv)) debug('args.keys() ' +repr(args.keys())) VERBOSE = args['verbosity'] cyclemethod = args['method'] or 3 header = args['header'] or '' answer = args['answer'] or '' output_fd = args['output_fd'] or 1 sargs = args['sargs'] if output_fd != 1: iOUT_FD = os.fdopen(int(output_fd), 'w') info('DEBUG: sargs=' + repr(sargs)) (use_uppercases, use_lowercases, use_decdigits, use_punct, use_whitespace, punct, punct2, whitespace, numbers, grid ) = make_grid(args) if answer: # for testing - bypass the grid and accept the answer password = answer exit_code = 0 else: if not stdscr: stdscr = curses.initscr() curses.noecho() curses.start_color() try: curses.use_default_colors() except: pass stdscr.keypad(1) for key, value in _curses.__dict__.items(): if key[0:4] == 'ACS_' or key in ('LINES', 'COLS'): if key == 'LINES': value = value - 3 setattr(curses, key, value) curses.flushinp () check_size(stdscr) pad = curses.newpad(100,100) if args['ttyalert'] == 'beep': curses.beep() elif args['ttyalert'] == 'flash': curses.flash() ##### MAIN LOOP: draw the interface and handle user input ##### cycle_index(cyclemethod) #Don't start the program with unrandomized indexes # If this isn't done, the program initially shows nothing main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, '') _previous_sigcont_handler = None while exit_code == -1: if hasattr(sys.stderr, 'getvalue'): title = sys.stderr.getvalue().strip() sys.stderr.buf = '' else: title = '' if title: title = title.split('\n')[-1] main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, title) curses.noecho() curses.cbreak() sys.stderr.flush() sys.stdout.flush() if not _previous_sigcont_handler: _previous_sigcont_handler = signal.getsignal(signal.SIGCONT) try: c = stdscr.getkey() except: quit_scr() break if c == '0': quit_scr(out) #on 'Done', write output and exit break if c in ['', '9', chr(27)]: # Quit quit_scr() #on 'Cancel', exit without writing output break if c == '8': # Hide hide_out -= hide_out+hide_out hide_out += 1 elif ((c == '7')&(len(out)>=1)) or ((c == 'KEY_BACKSPACE')&(len(out)>=1)): out = out[:len(out)-1] # Delete elif Dialog is not None and c == '6': # File Contents # you will first need to save the tty modes with a call to def_prog_mode() # and then call endwin() to end the curses mode. This will leave you in the # original tty mode. To get back to curses once you are done, call # reset_prog_mode() . This function returns the tty to the state stored by # def_prog_mode(). Then do refresh(), and you are back to the curses mode. _curses.def_prog_mode() ; _curses.endwin() sBut, sCode = lDialogFselect(oLAST_DIR, height=10, width=50, help_button=False) if sBut == 'ok': sOut = sCode else: return sBut = 'ok'; sCode = '=' if sOut and out: sText = sOut # "New text at Beginning, Replace or End" sBut, sCode = lDialogCode(sText) if sBut != 'ok': pass elif sCode == '<': out = sOut + ' ' + out elif sCode == '>': out = out + ' ' + sOut elif sCode == '=': out = sOut elif sCode == '': out = '' # oDIALOG.msgbox("Bug Alert: If the screen goes black, suspend to shell with ^Z and then use fg to come back to Gridfire " +sCode, height=8, width=40) main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, '') _curses.reset_prog_mode() stdscr.refresh() elif Dialog is not None and c == '5': # File Name # you will first need to save the tty modes with a call to def_prog_mode() # and then call endwin() to end the curses mode. This will leave you in the # original tty mode. To get back to curses once you are done, call # reset_prog_mode() . This function returns the tty to the state stored by # def_prog_mode(). Then do refresh(), and you are back to the curses mode. _curses.def_prog_mode() ; _curses.endwin() sOut = sDialogFget() sBut = 'ok'; sCode = '=' if sOut and out: sText = sOut # "New text at Beginning, Replace or End" sBut, sCode = lDialogCode(sText) if sBut != 'ok': pass elif sCode == '<': out = sOut + ' ' + out elif sCode == '>': out = out + ' ' + sOut elif sCode == '=': out = sOut elif sCode == '': out = '' # oDIALOG.msgbox("Bug Alert: If the screen goes black, suspend to shell with ^Z and then use fg to come back to Gridfire " +sCode, height=8, width=40) main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, '') _curses.reset_prog_mode() stdscr.refresh() elif Dialog is not None and c == '4': # Shell if oDIALOG is None: oDIALOG = Dialog() sCode = '=' sOut = '' yStdoutdata = yStderrdata = b'' # you will first need to save the tty modes with a call to def_prog_mode() # and then call endwin() to end the curses mode. This will leave you in the # original tty mode. To get back to curses once you are done, call # reset_prog_mode() . This function returns the tty to the state stored by # def_prog_mode(). Then do refresh(), and you are back to the curses mode. _curses.def_prog_mode() ; _curses.endwin() sCode, sAnswer = oDIALOG.inputbox("Run command (erase to cancel)", init='') if sCode == 'ok' and sAnswer: try: devnull = subprocess.DEVNULL except AttributeError: # Python < 3.3 devnull_context = devnull = open(os.devnull, "wb") else: devnull_context = DummyContextManager() with devnull_context: p = subprocess.Popen(sAnswer, stdout=subprocess.PIPE, stderr=devnull, close_fds=True) if True: ( yStdoutdata, yStderrdata ) = p.communicate() retcode = p.returncode oDIALOG.scrollbox(yStdoutdata.decode().strip(), 15,50) else: retcode = p.wait() ystdoutdata = p.stdout.read() # One could use title=... instead of text=... to put the text # in the title bar. oDIALOG.programbox(fd=p.stdout.fileno(), text="Output of command: " +sAnswer) # oDIALOG.msgbox("Bug Alert: If the screen goes black, suspend to shell with ^Z and then use fg to come back to Gridfire " +str(retcode), height=8, width=40) if retcode == 0: sOut = yStdoutdata.decode().strip() sBut = 'ok'; sCode = '=' if sOut and out: sText = sOut # "New text at Beginning, Replace or End" sBut, sCode = lDialogCode(sText) if sBut != 'ok': pass elif sCode == '<': out = sOut + ' ' + out elif sCode == '>': out = out + ' ' + sOut elif sCode == '=': out = sOut elif sCode == '': out = '' _curses.reset_prog_mode() stdscr.refresh() elif c == '3': # was Ignore # now 'Ignore' is a keypair to fool keystroke counting pass elif PinEntry is not None and c == '2': # unused if oPinEntry is None: oPinEntry = PinEntry() oPinEntry.run() # unfinished #handle row/column selections elif (uppercase.find(c)+1): upperchar = c elif (row_index[:total_rows(use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct)].find(c)+1): lowerchar = c #if selection completes a pair, find choice, add to output, reset selection if len(upperchar)&len(lowerchar): #if selected pair chooses 'Done' or 'Cancel' if((upperchar==col_index[0])&(lowerchar==row_index[len(grid)+1])): #on 'Ignore', do nothing pass # :Space :Tab :Enter elif((upperchar==col_index[0])&(lowerchar==row_index[len(grid)])): out += ' ' #on 'Space' elif((upperchar==col_index[1])&(lowerchar==row_index[len(grid)])): out += ' ' #on 'Tab' elif((upperchar==col_index[2])&(lowerchar==row_index[len(grid)])): out += '\n' #on 'Enter' # elif((upperchar==col_index[2])&(lowerchar==row_index[len(grid)])): # out = out[:len(out)-1] #on 'Delete' else: rowchoice = row_index.find(lowerchar) if rowchoice < len(grid): if (grid[rowchoice].find("A") == 0): out += uppercase[col_index.find(upperchar)] if (grid[rowchoice].find("a") == 0): out += lowercase[col_index.find(upperchar)] if (grid[rowchoice].find("0") == 0)&(spaceout(col_index).find(upperchar) < len(numbers)): out += numbers[spaceout(col_index).find(upperchar)] if (grid[rowchoice].find("!") == 0): out += punct[spaceout(col_index).find(upperchar)] if (grid[rowchoice].find("_") == 0)&(spaceout(col_index).find(upperchar) < len(punct2)): out += punct2[spaceout(col_index).find(upperchar)] upperchar = '' lowerchar = '' cycle_index(cyclemethod) single = args['single'] or '' double = args['double'] or '' repeat = args['repeat'] or '' if single: thing = ['-'+single, password] sStdIn = None elif double: thing = ['--'+double, password] sStdIn = None elif repeat: thing = [] sStdIn = password +'\n' +password else: thing = [] sStdIn = password if exit_code == 0 and sargs and password: # sys.stderr.write('EXIT: ' + repr(password) + '\n') if type(sargs) == str: #? quote largs = sargs.split(' ') elif type(sargs) == list and len(sargs) == 1 and sargs[0].find(' ') > 0: largs = sargs[0].strip().split() elif type(sargs) == list and len(sargs) > 1: largs = sargs else: raise RuntimeError('ERROR: not list or str type(sargs) ' + str(type(sargs) )) if thing: pos = args['pos'] if pos == -1: largs.append(thing) else: largs.insert(pos, thing) shell = False executable = '' # largs[0] sargs = ' '.join(largs[1:]) (exit_code, password,) = lSubprocess(largs, shell, executable, sStdIn=sStdIn) else: debug('len(password)=%d' % len(password)) # del password, exit_code stdscr = None cycle_index(cyclemethod) #Don't start the program with unrandomized indexes retval = [exit_code, password][:] out = '' exit_code = -1 return retval # should be getpass.getpass compatible def getpass(prompt="", stream=None, tty=None, main_args=None): # what should stream be if curses should be a tty if tty is not None: pass elif stream is not None: tty = os.ttyname(stream.fileno()) elif 'GPG_TTY' in os.environ: tty = os.environ['GPG_TTY'] else: tty = os.ttyname(sys.stdin.fileno()) with open(tty, 'r') as fd: assert fd.isatty(), "The input stream must be a tty: %r" % (tty,) if not hasattr(sys, '_stderr'): sys._stderr = sys.stderr sys.stderr = StringIO() password = '' exit_code = -1 if main_args is None: main_args = ['-H', prompt] else: main_args += ['-H', prompt] try: (exit_code, password,) = main(main_args) info('EXIT: getpass ' + prompt +' ' + repr(password)) except KeyboardInterrupt: info('EXIT: getpass KeyboardInterrupt') if hasattr(sys, '_stderr'): sys.stderr = sys._stderr return password def sDialogFget(): global oLAST_DIR sBut, sCode = lDialogFselect(oLAST_DIR, height=10, width=50, help_button=False) sOut = '' if sBut == 'ok': sFile = sCode try: with open( sFile, 'rt') as iFd: sOut = iFd.read().strip() except Exception as e: # dunno pass else: if os.path.isdir(sFile): oLAST_DIR = sFile else: oLAST_DIR = os.path.dirname(sFile) oLAST_DIR = oLAST_DIR + '/' return sOut def lDialogFselect(sLastDir=None, height=10, width=50, help_button=False, prompt=None): global oDIALOG, oLAST_DIR if oDIALOG is None: oDIALOG = Dialog() if not sLastDir: sLastDir = oLAST_DIR sBut, sCode = oDIALOG.fselect(sLastDir, height, width, help_button=False) return (sBut, sCode,) def lDialogCode(sText): lChoices = [("<", "Begin - Add the text at the beginning"), ("=", "All - Replace all with the text"), (">", "End - Add the text at the end")] sCode = '=' sBut, sCode = oDIALOG.menu(sText, height=16, width=50, choices=lChoices, title="Replace the buffer with file contents", help_button=False) return (sBut, sCode,) def lSubprocess(largs, shell=False, executable='', sStdIn=''): env = os.environ.copy() if 'PYTHONPATH' in env: del env['PYTHONPATH'] debug('executable=' + repr(executable) +' largs=' + repr(largs) ) # + ' len(password)=%d' % len(password) yStdoutData = stderrdata = '' try: oProcess = subprocess.Popen(largs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, shell=shell) sleep(3) except Exception as e: error('Popen executable=' + repr(executable) +' largs=' + repr(largs) + '\n' + str(e)) exit_code = 1 sStdOut = '' else: if sStdIn: (yStdoutData, stderrdata,) = oProcess.communicate(sStdIn.encode()) else: #? wait (yStdoutData, stderrdata,) = oProcess.communicate() #? oProcess.close() if stderrdata: sys.stderr.write(stderrdata.decode() + '\n') exit_code = oProcess.returncode sStdOut = yStdoutData.decode() return (exit_code, sStdOut,) def main(): sargs = sys.argv[1:] if type(sargs) == list and len(sargs) == 1 and sargs[0].find(' ') > 0: lSysArgv = sargs[0].strip().split() else: lSysArgv = sargs # print('DEBUG: ' +repr(sys.argv[1:]) +' ' +repr(type(sys.argv[1:]))) if hasattr(sys, 'frozen') and hasattr(sys, '_MEIPASS'): print('DEBUG: running in a PyInstaller bundle') (exit_code, password,) = iMain(lSysArgv) iOUT_FD.write(password+('\n'*newline)) password = '' sys.stderr.flush() return exit_code if __name__ == '__main__': sys.exit(main())