2020-09-10 21:32:59 +00:00
import os , sys ; sys . path . append ( os . path . abspath ( os . path . join ( os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , ' .. ' ) ) , ' .. ' ) ) )
from komrade import *
from komrade . backend import *
import art
2020-09-11 14:35:47 +00:00
import textwrap as tw
2020-09-14 20:18:47 +00:00
2020-09-10 21:32:59 +00:00
2020-09-14 20:32:17 +00:00
2020-09-10 21:32:59 +00:00
class CLI ( Logger ) :
ROUTES = {
2020-09-13 13:58:58 +00:00
' help ' : ' seek help ' ,
2020-09-16 10:23:18 +00:00
' signup ' : ' join the komrades ' ,
2020-09-13 18:06:41 +00:00
' login ' : ' log back in ' ,
2020-09-14 08:48:23 +00:00
' meet ' : ' meet a komrade ' ,
2020-09-14 20:10:31 +00:00
' who ' : ' show contacts or info ' ,
2020-09-14 22:00:13 +00:00
' msg ' : ' write people ' ,
2020-09-16 10:23:18 +00:00
' check ' : ' check mail ' ,
2020-09-15 10:17:06 +00:00
' read ' : ' read mail ' ,
2020-09-15 08:51:52 +00:00
' verbose ' : ' show/hide log output '
2020-09-10 21:32:59 +00:00
}
2020-09-12 07:55:23 +00:00
def __init__ ( self , name = ' ' , cmd = ' ' , persona = None ) :
2020-09-13 18:47:25 +00:00
self . name = name
2020-09-12 07:55:23 +00:00
self . cmd = cmd
2020-09-13 18:47:25 +00:00
self . komrade = None
self . loggedin = False
2020-09-10 21:32:59 +00:00
2020-09-15 08:51:52 +00:00
def verbose ( self , * x ) :
self . toggle_log ( )
2020-09-11 14:35:47 +00:00
def run ( self , inp = ' ' , name = ' ' ) :
2020-09-13 18:47:25 +00:00
# if name: self.name=name
2020-09-13 14:34:54 +00:00
# clear_screen()
# self.boot()
2020-09-15 08:51:52 +00:00
if not inp :
self . help ( )
2020-09-10 21:32:59 +00:00
2020-09-15 10:33:17 +00:00
if inp :
2020-09-15 10:35:04 +00:00
for inpx in inp . split ( ' / ' ) :
self . route ( ' / ' + inpx . strip ( ) )
2020-09-10 21:32:59 +00:00
while True :
try :
2020-09-16 12:49:46 +00:00
inp = input ( f ' Komrade @ { self . name if self . name else " ? " } : ' )
2020-09-16 10:23:18 +00:00
# self.print(inp,'??')
2020-09-15 08:51:52 +00:00
self . route ( inp )
2020-09-13 14:48:26 +00:00
except ( KeyboardInterrupt , EOFError ) as e :
2020-09-16 12:41:42 +00:00
exit ( ' \n \n Komrade @Operator: Goodbye. \n ' )
2020-09-15 08:51:52 +00:00
except KomradeException as e :
2020-09-16 12:41:42 +00:00
self . print ( f ' Komrade @Operator: I could not handle your request. { e } \n ' )
2020-09-11 14:35:47 +00:00
#await asyncio.sleep(0.5)
2020-09-10 21:32:59 +00:00
def route ( self , inp ) :
inp = inp . strip ( )
2020-09-16 10:23:18 +00:00
# self.print('route got:',[inp])
2020-09-10 21:32:59 +00:00
if not inp . startswith ( ' / ' ) : return
cmd = inp . split ( ) [ 0 ]
dat = inp [ len ( cmd ) : ] . strip ( )
cmd = cmd [ 1 : ]
2020-09-16 10:23:18 +00:00
# self.print([cmd,dat])
2020-09-10 21:32:59 +00:00
if cmd in self . ROUTES and hasattr ( self , cmd ) :
f = getattr ( self , cmd )
2020-09-16 10:23:18 +00:00
try :
res = f ( dat )
except KomradeException as e :
2020-09-16 12:41:42 +00:00
self . stat ( ' Message not sent. ' , e , ' \n ' )
2020-09-16 10:23:18 +00:00
2020-09-16 15:20:02 +00:00
def stat ( self , * msgs , use_prefix = True , prefix = None , komrade_name = None , pause = False , clear = False , min_prefix_len = 19 , * * kwargs ) :
2020-09-16 15:06:37 +00:00
if not prefix :
if not komrade_name : komrade_name = ' Operator '
2020-09-16 15:10:19 +00:00
prefix = ' Komrade @ ' + komrade_name + ' : '
2020-09-16 15:14:30 +00:00
blank = ' ' * ( len ( prefix ) if len ( prefix ) > min_prefix_len else min_prefix_len )
2020-09-16 12:41:42 +00:00
total_msg = [ ]
for i , msg in enumerate ( msgs ) :
2020-09-16 14:57:21 +00:00
msg = msg . replace ( ' \r \n ' , ' \n ' ) . replace ( ' \r ' , ' \n ' )
2020-09-16 14:57:38 +00:00
for ii , ln in enumerate ( msg . split ( ' \n ' ) ) :
2020-09-16 15:14:30 +00:00
if not ln :
total_msg + = [ ' ' ]
2020-09-16 14:57:21 +00:00
ln_wrap = tw . wrap ( ln , CLI_WIDTH - len ( prefix ) )
for iii , lnw in enumerate ( ln_wrap ) :
2020-09-16 15:15:58 +00:00
prfx = prefix + ( ' ' * ( len ( blank ) - len ( prefix ) ) ) if ( not i and not ii and not iii and use_prefix ) else blank
2020-09-16 14:57:53 +00:00
x = prfx + lnw
2020-09-16 14:57:21 +00:00
total_msg + = [ x ]
2020-09-16 12:41:42 +00:00
total_msg + = [ ' ' ]
2020-09-16 12:49:46 +00:00
# print()
2020-09-16 14:57:21 +00:00
# print([total_msg])
2020-09-16 14:54:50 +00:00
print ( ' \n ' . join ( total_msg ) )
2020-09-16 15:06:37 +00:00
if pause : do_pause ( )
if clear : clear_screen ( )
2020-09-16 10:23:18 +00:00
def print ( self , * x ) :
2020-09-16 14:54:17 +00:00
print ( * x )
# x=' '.join(str(xx) for xx in x)
# x=str(x).replace('\r\n','\n').replace('\r','\n')
# for ln in x.split('\n'):
# # #scan_print(ln+'\n\n')
# if not ln: print()
# for ln2 in tw.wrap(ln,CLI_WIDTH):
# print(ln2)
# # x='\n'.join(tw.wrap(x,CLI_WIDTH))
# # print(x)
2020-09-16 10:23:18 +00:00
def boot ( self , indent = None ) :
if indent is None :
indent = int ( round ( CLI_WIDTH * .18333333 ) )
logo = art . text2art ( CLI_TITLE , font = CLI_FONT ) . replace ( ' \r \n ' , ' \n ' )
border = ' - ' * CLI_WIDTH # (len(logo.strip().split('\n')[0]))
2020-09-11 17:17:21 +00:00
# logo=make_key_discreet_str(logo,chance_redacted=0.1) #.decode()
2020-09-13 14:55:17 +00:00
logo = tw . indent ( logo , ' ' * indent )
2020-09-16 10:23:18 +00:00
border = tw . indent ( border , ' ' * 2 )
print ( ' \n ' + logo ) #),max_pause=0.005)
2020-09-10 21:32:59 +00:00
2020-09-13 14:34:17 +00:00
def help ( self , * x , * * y ) :
2020-09-13 14:34:36 +00:00
clear_screen ( )
self . boot ( )
2020-09-14 20:18:47 +00:00
2020-09-16 10:23:18 +00:00
border = ' - ' * ( 40 )
if not self . logged_in :
HELPSTR = f """
/ login [ name ] - - > log back in
/ signup [ name ] - - > new komrade
2020-09-14 20:18:47 +00:00
"""
2020-09-16 10:23:18 +00:00
else :
HELPSTR = f """
2020-09-16 13:09:21 +00:00
/ check - - > check messages
/ read - - > read messages
/ msg [ name ] - - > send message
2020-09-16 10:23:18 +00:00
/ meet [ name ] - - > exchange info
/ who [ name ] - - > show contacts
"""
HELPSTR + = f """
/ help - - > seek help
"""
helpstr = tw . indent ( HELPSTR . strip ( ) + ' \n \n ' , ' ' * 13 )
self . print ( helpstr )
# self.print(border+helpstr+'\n'+self.border)
@property
def border ( self ) :
border = ' - ' * CLI_WIDTH # (len(logo.strip().split('\n')[0]))
border = tw . indent ( border , ' ' * 2 )
return border
2020-09-10 21:32:59 +00:00
def intro ( self ) :
2020-09-14 08:48:23 +00:00
self . status ( None )
2020-09-14 08:48:40 +00:00
def who ( self , whom ) :
2020-09-14 08:48:23 +00:00
if self . with_required_login ( ) :
contacts = self . komrade . contacts ( )
2020-09-16 10:23:18 +00:00
self . print ( ' ' + ' \n ' . join ( contacts ) )
2020-09-14 08:48:23 +00:00
2020-09-10 21:32:59 +00:00
2020-09-16 10:23:18 +00:00
def signup ( self , name = None ) :
2020-09-13 18:20:19 +00:00
if not name : name = input ( ' name: ' )
if not name : return
2020-09-13 18:47:25 +00:00
self . komrade = Komrade ( name )
2020-09-16 14:50:05 +00:00
was_off = self . off
2020-09-16 14:50:50 +00:00
# if was_off: self.show_log()
2020-09-16 15:08:57 +00:00
def logfunc ( * x , komrade_name = ' Keymaker ' , * * y ) :
self . stat ( * x , komrade_name = komrade_name , * * y )
2020-09-16 14:52:24 +00:00
res = self . komrade . register ( logfunc = logfunc )
2020-09-16 14:50:50 +00:00
# if was_off: self.toggle_log()
2020-09-13 14:35:41 +00:00
if res and type ( res ) == dict and ' success ' in res and res [ ' success ' ] :
2020-09-13 18:47:25 +00:00
self . name = self . komrade . name
self . loggedin = True
2020-09-16 12:49:46 +00:00
self . help ( )
2020-09-16 10:23:18 +00:00
self . stat ( f ' Welcome, Komrade @ { self . name } . ' )
2020-09-13 14:29:53 +00:00
else :
2020-09-13 18:47:25 +00:00
self . name = None
self . loggedin = False
self . komrade = None
2020-09-14 06:20:02 +00:00
if res and ' status ' in res :
# self.boot()
2020-09-16 10:23:18 +00:00
self . stat ( res . get ( ' status ' , ' ? ' ) )
2020-09-14 06:20:02 +00:00
2020-09-13 14:15:06 +00:00
def login ( self , name ) :
2020-09-16 10:23:18 +00:00
# self.print(self,name,self.name,self.komrade,self.loggedin)
2020-09-13 14:25:00 +00:00
if not name : name = input ( ' name: ' )
if not name : return
2020-09-13 18:47:25 +00:00
self . komrade = Komrade ( name )
2020-09-13 18:20:19 +00:00
2020-09-13 18:47:25 +00:00
res = self . komrade . login ( )
2020-09-16 12:41:42 +00:00
# print('got login res:',res)
self . log ( ' <- komrade.login() <- ' , res )
2020-09-13 18:20:19 +00:00
2020-09-13 14:35:41 +00:00
if res and type ( res ) == dict and ' success ' in res and res [ ' success ' ] :
2020-09-13 18:47:25 +00:00
self . name = self . komrade . name
self . loggedin = True
2020-09-13 14:29:53 +00:00
else :
2020-09-13 18:47:25 +00:00
self . name = None
self . loggedin = False
self . komrade = None
2020-09-14 07:58:26 +00:00
if res and ' status ' in res :
2020-09-15 10:23:32 +00:00
self . help ( )
2020-09-16 10:23:18 +00:00
self . stat ( res . get ( ' status ' , ' ? ' ) )
2020-09-13 14:15:06 +00:00
2020-09-16 12:41:42 +00:00
# also see if we got a msg update
if ' res_refresh ' in res :
self . check (
res = res [ ' res_refresh ' ] ,
statd = { ' use_prefix ' : True }
)
2020-09-13 18:06:41 +00:00
@property
def logged_in ( self ) :
2020-09-13 18:47:25 +00:00
return ( self . loggedin and self . komrade and self . name )
2020-09-13 18:06:41 +00:00
2020-09-14 08:48:23 +00:00
2020-09-14 20:18:47 +00:00
def with_required_login ( self , quiet = False ) :
2020-09-13 18:06:41 +00:00
if not self . logged_in :
2020-09-14 20:18:47 +00:00
if not quiet :
2020-09-16 10:23:18 +00:00
self . stat ( ' You must be logged in first. ' )
2020-09-14 08:48:23 +00:00
return False
return True
2020-09-15 11:29:16 +00:00
# def meet(self,name):
2020-09-14 08:48:23 +00:00
2020-09-15 11:29:16 +00:00
# if not name:
# name=input(f'@Operator: To whom would you like to introduce yourself?\n\n@{self.name}: ')
# if not name: return
2020-09-13 18:06:41 +00:00
2020-09-15 11:29:16 +00:00
# # meet?
# self.komrade.meet(name)
2020-09-13 17:42:05 +00:00
2020-09-15 15:07:15 +00:00
def meet ( self , dat , returning = False ) :
2020-09-15 11:12:49 +00:00
if self . with_required_login ( ) :
2020-09-16 13:38:15 +00:00
datl = dat . strip ( ) . split ( )
if not datl :
self . stat ( ' Meet whom? ' )
return
name_or_pubkey = datl [ 0 ]
2020-09-15 15:07:15 +00:00
res = self . komrade . meet ( name_or_pubkey , returning = returning )
2020-09-16 10:23:18 +00:00
status = res . get ( ' status ' )
2020-09-16 10:42:54 +00:00
#msg = status if not res.get('success') else status+str(res)
if res . get ( ' success ' ) :
2020-09-16 10:46:24 +00:00
self . stat ( f ' I sent the following message to @ { name_or_pubkey } : \n \n " { res . get ( " msg_sent " ) } " ' )
2020-09-16 10:42:54 +00:00
else :
self . stat ( status )
2020-09-15 11:12:49 +00:00
2020-09-14 20:10:31 +00:00
def msg ( self , dat ) :
2020-09-14 20:18:47 +00:00
if self . with_required_login ( ) :
2020-09-16 10:23:18 +00:00
dat = dat . strip ( )
if not dat :
self . status ( ' Message whom? Usage: /msg [name] ' )
return
datl = dat . split ( ' ' , 1 )
name_or_pubkey = datl [ 0 ]
if len ( datl ) == 1 :
self . stat ( f ' Compose your message to @ { name_or_pubkey } below. ' , ' Press Ctrl+D to complete, or Ctrl+C to cancel. ' )
msg_s = multiline_input ( ) . strip ( )
if not msg_s :
print ( ' \n ' )
self . stat ( ' Not sending. No message found. ' )
return
else :
msg_s = datl [ 1 ]
self . log ( f ' Composed msg to { name_or_pubkey } : { msg_s } ' )
2020-09-14 20:18:47 +00:00
msg_obj = self . komrade . msg (
name_or_pubkey ,
2020-09-16 10:23:18 +00:00
msg_s
2020-09-14 20:18:47 +00:00
)
self . log ( f ' Sent msg obj to { name_or_pubkey } : { msg_obj } ' )
2020-09-16 13:46:54 +00:00
self . stat ( f ' Message successfully sent to @ { name_or_pubkey } . ' )
2020-09-13 17:42:05 +00:00
2020-09-16 12:41:42 +00:00
def check ( self , dat = None , res = None , statd = { } ) :
self . log ( f ' <-- dat= { dat } , res= { res } ' )
if not res :
if self . with_required_login ( ) :
res = self . komrade . refresh ( )
if not res [ ' success ' ] :
self . stat ( res [ ' status ' ] )
return
unr = res . get ( ' unread ' , [ ] )
inb = res . get ( ' inbox ' , [ ] )
2020-09-16 12:49:46 +00:00
self . stat ( f ' You have { len ( unr ) } unread messages, with { len ( inb ) } total in your inbox. ' , * * statd )
2020-09-16 12:41:42 +00:00
self . log ( f ' --> unr= { unr } , inb= { inb } ' )
# stop
2020-09-15 10:17:06 +00:00
2020-09-15 13:31:31 +00:00
def prompt_adduser ( self , msg ) :
2020-09-16 10:23:18 +00:00
# self.print('prompt got:',msg)
# self.print(msg.data)
2020-09-15 14:03:00 +00:00
do_pause ( )
2020-09-16 13:46:54 +00:00
# clear_screen()
2020-09-15 13:55:23 +00:00
meet_name = msg . data . get ( ' meet_name ' )
meet_uri = msg . data . get ( ' meet ' )
2020-09-15 14:09:33 +00:00
qrstr = self . komrade . qr_str ( meet_uri )
2020-09-16 12:41:42 +00:00
self . stat ( f " Add @ { meet_name } ' s public key to your address book? " , f ' It will allow you and @ { meet_name } to read and write encrypted messages to one another. ' )
do_adduser = input ( f ''' \n { self . komrade } [y/N]: ''' )
2020-09-15 13:52:35 +00:00
if do_adduser . strip ( ) . lower ( ) == ' y ' :
2020-09-15 14:05:33 +00:00
fnfn = self . komrade . save_uri_as_qrcode (
2020-09-15 13:31:31 +00:00
meet_uri ,
meet_name
)
2020-09-15 14:13:25 +00:00
clear_screen ( )
2020-09-16 12:41:42 +00:00
self . stat ( f ' The public key of @ { meet_name } has been saved as a QRcode to { fnfn } ' )
print ( qrstr )
2020-09-16 10:36:16 +00:00
do_pause ( )
clear_screen ( )
2020-09-16 12:41:42 +00:00
self . stat ( ' Send this user your public key as well? ' )
do_senduser = input ( f ''' \n { self . komrade } [y/N]: ''' )
2020-09-16 10:36:16 +00:00
2020-09-16 08:30:07 +00:00
if do_senduser . strip ( ) . lower ( ) == ' y ' :
2020-09-15 15:11:31 +00:00
res = self . komrade . meet ( meet_name , returning = True )
2020-09-16 10:36:16 +00:00
if res . get ( ' success ' ) :
2020-09-16 12:41:42 +00:00
self . stat ( ' Returning the invitation: ' , f ' " { res . get ( " msg_sent " ) } " ' , use_prefix = True )
2020-09-16 13:46:54 +00:00
do_pause ( )
2020-09-16 10:36:16 +00:00
else :
self . stat ( msg . get ( ' status ' ) )
2020-09-15 13:31:31 +00:00
2020-09-15 14:02:42 +00:00
def prompt_msg ( self , msg ) :
2020-09-16 13:46:54 +00:00
clear_screen ( )
print ( msg )
2020-09-16 12:41:42 +00:00
self . stat ( ' Type " r " to reply to this message, " d " to delete it, or hit Enter to continue. ' )
do = input ( f ' \n { self . komrade } : ' )
2020-09-15 14:02:42 +00:00
do = do . strip ( ) . lower ( )
if do == ' d ' :
2020-09-16 10:23:18 +00:00
# self.print('del',msg.post_id)
2020-09-16 12:41:42 +00:00
res = self . komrade . delete_msg ( msg . post_id )
if res . get ( ' success ' ) :
self . stat ( ' Deleted message. ' )
else :
self . stat ( ' Could not delete message. ' )
do_pause ( )
2020-09-15 14:02:42 +00:00
elif do == ' r ' :
2020-09-16 10:23:18 +00:00
self . print ( ' @todo: replying... ' )
2020-09-15 14:02:42 +00:00
else :
pass
2020-09-15 13:31:31 +00:00
2020-09-15 10:17:06 +00:00
def read ( self , dat ) :
if self . with_required_login ( ) :
2020-09-15 12:07:09 +00:00
res = self . komrade . inbox ( )
2020-09-16 10:23:18 +00:00
# self.print('got from read res:',res)
2020-09-15 12:07:09 +00:00
if not res . get ( ' success ' ) :
2020-09-16 10:23:18 +00:00
self . print ( ' @Operator: ' , res [ ' status ' ] )
2020-09-15 12:07:09 +00:00
return
2020-09-15 13:40:48 +00:00
2020-09-16 10:23:18 +00:00
# self.print('ummmmm msgs?')
2020-09-15 13:17:24 +00:00
msgs = res . get ( ' msgs ' )
2020-09-15 10:17:06 +00:00
if not msgs :
2020-09-16 10:23:18 +00:00
self . print ( ' @Operator: No messages. ' )
2020-09-15 10:17:06 +00:00
else :
clear_screen ( )
for i , msg in enumerate ( msgs ) :
2020-09-16 14:34:21 +00:00
try :
self . stat ( f ' Showing message { i + 1 } of { len ( msgs ) } , from newest to oldest. Hit Ctrl+D to exit. ' )
print ( )
print ( msg )
# self.print('DATA',msg.msg_d)
if msg . data . get ( ' prompt_id ' ) == ' addcontact ' :
self . prompt_adduser ( msg )
self . prompt_msg ( msg )
clear_screen ( )
except EOFError :
break
2020-09-15 10:54:15 +00:00
self . help ( )
2020-09-15 10:17:06 +00:00
2020-09-13 17:42:05 +00:00
2020-09-13 14:15:06 +00:00
2020-09-11 14:35:47 +00:00
### DIALOGUES
2020-09-10 21:32:59 +00:00
2020-09-11 14:35:47 +00:00
# hello, op?
2020-09-11 17:17:21 +00:00
def status_keymaker_part1 ( self , name ) :
2020-09-11 14:35:47 +00:00
self . status ( None , { ART_OLDPHONE4 + ' \n ' , True } , 3 ) #,scan=False,width=None,pause=None,clear=None)
2020-09-10 21:32:59 +00:00
nm = name if name else ' ? '
self . status (
2020-09-11 14:35:47 +00:00
f ' \n \n \n @ { nm } : Uh yes hello, Operator? I would like to join Komrade, the socialist network. Could you patch me through? ' , clear = False )
2020-09-10 21:32:59 +00:00
while not name :
name = self . status ( ( ' name ' , ' @TheTelephone: Of course, Komrade...? \n @ ' ) ) . get ( ' vals ' ) . get ( ' name ' ) . strip ( )
2020-09-16 10:23:18 +00:00
self . print ( )
2020-09-10 21:32:59 +00:00
self . status (
f ' @TheTelephone: Of course, Komrade @ { name } . A fine name. ' ,
''' @TheTelephone: However, I ' m just the local operator who lives on your device; my only job is to communicate with the remote operator securely. ''' ,
''' Komrade @TheOperator lives on the deep web. She ' s the one you want to speak with. ''' ,
2020-09-11 14:35:47 +00:00
None , { ART_OLDPHONE4 } , f ''' @ { name } : Hm, ok. Well, could you patch me through to the remote operator then? ''' ,
2020-09-10 21:32:59 +00:00
2020-09-13 18:47:25 +00:00
f ''' @ { TELEPHONEname } : I could, but it ' s not safe yet. Your information could be exposed. You need to cut your encryption keys first. ''' ,
2020-09-10 21:32:59 +00:00
f ' @ { name } : Fine, but how do I do that? ' ,
2020-09-13 18:47:25 +00:00
f ' @ { TELEPHONEname } : Visit the Keymaker. ' ,
2020-09-10 21:32:59 +00:00
clear = False , pause = True )
### KEYMAKER
2020-09-11 14:35:47 +00:00
self . status ( None , { tw . indent ( ART_KEY , ' ' * 5 ) + ' \n ' , True } , 3 ) #,clear=False,indent=10,pause=False)
2020-09-10 21:32:59 +00:00
# convo
2020-09-11 14:35:47 +00:00
self . status (
f ' \n @ { name } : Hello, Komrade @Keymaker? I would like help forging a new set of keys. ' ,
2020-09-10 21:32:59 +00:00
f ' @Keymaker: Of course, Komrade @ { name } . ' ,
2020-09-11 14:35:47 +00:00
)
2020-09-10 21:32:59 +00:00
2020-09-11 17:17:21 +00:00
self . status (
2020-09-12 07:55:23 +00:00
' I will cut for you two matching keys, part of an " asymmetric " pair. ' ,
2020-09-11 17:17:21 +00:00
' Please, watch me work. ' ,
2020-09-11 14:35:47 +00:00
2020-09-11 17:17:21 +00:00
None , { tw . indent ( ART_KEY , ' ' * 5 ) + ' \n ' } ,
2020-09-11 14:35:47 +00:00
2020-09-11 17:17:21 +00:00
' I use a high-level cryptographic function from Themis, a well-respected open-source cryptography library. ' ,
' I use the iron-clad Elliptic Curve algorthm to generate the asymmetric keypair. ' ,
' > GenerateKeyPair(KEY_PAIR_TYPE.EC) ' ,
3
)
self . status (
None ,
{ ART_KEY_PAIR , True }
) #,clear=False,indent=10,pause=False)
2020-09-12 07:55:23 +00:00
return name
def status_keymaker_part2 ( self , name , passphrase , pubkey , privkey , hasher , persona ) :
from getpass import getpass
# gen what we need
uri_id = pubkey . data_b64
qr_str = get_qr_str ( uri_id )
qr_path = os . path . join ( PATH_QRCODES , name + ' .png ' )
# what are pub/priv?
# self.status(
# None,{ART_KEY_PAIR},
# 'A matching set of keys have been generated.',
# None,{ART_KEY_PAIR2A+'\nA matching set of keys have been generated.'+'\n'},
# 'First, I have made a "public key" which you can share with anyone:',
# f'(1) {pubkey.data_b64.decode()}',
# 'This key is a randomly-generated binary string, which acts as your "address" on Komrade.',
# 'With it, someone can write you an encrypted message which only you can read.'
# )
# self.status(
# None,{ART_KEY_PAIR2A},
# f'You can share your public key by copy/pasting it to them over a secure channel (e.g. Signal).',
# 'Or, you can share it as a QR code, especially IRL:',
# {qr_str+'\n\n',True,5},
# f'\n\n(If registration is successful, this QR code be saved as an image to your device at: {qr_path}.)'
# )
2020-09-10 21:32:59 +00:00
2020-09-11 14:35:47 +00:00
# private keys
2020-09-11 17:17:21 +00:00
self . status ( None ,
{ ART_KEY_PAIR2B } ,
2020-09-12 07:55:23 +00:00
' Second, I have cut a matching " private key " . ' ,
" It ' s too dangerous to show in full, so here it is 66 % r edacted: " ,
f ' (2) { make_key_discreet ( privkey . data_b64 , 0.3 ) } ' ,
' With it, you can decrypt and read any message sent to you via your public key. ' ,
' You can also encrypt and send messages to other people whose public keys you have. ' ,
2020-09-11 17:17:21 +00:00
)
2020-09-12 07:55:23 +00:00
# private keys
2020-09-11 17:17:21 +00:00
self . status ( None ,
2020-09-12 07:55:23 +00:00
{ CUBEKEY } ,
' So if someone were to steal your private key, they could read your mail and forge your signature. '
' You you should never, ever give your private key to anyone. ' ,
' In fact, this key is so dangerous that we need to lock it away immediately. ' ,
" We ' ll even throw away the key we use to lock this private key with! " ,
" How? By regenerating it each time from your password. " ,
2020-09-10 21:32:59 +00:00
)
2020-09-11 14:35:47 +00:00
if not passphrase :
2020-09-11 17:17:21 +00:00
from getpass import getpass
2020-09-11 14:35:47 +00:00
self . status (
' And it looks like you haven \' t yet chosen a password. ' ,
3 , " Don ' t tell it to me! Never tell it to anyone. " ,
" Ideally, don ' t even save it on your computer; just remember it, or write it down on paper. " ,
" Instead, whisper it to Komrade @Hasher, who scrambles information ' 1-way ' , like a blender. " ,
)
2020-09-11 17:17:21 +00:00
2020-09-11 14:35:47 +00:00
res = self . status ( None ,
2020-09-11 17:17:21 +00:00
{ indent_str ( ART_FROG_BLENDER , 10 ) , True } ,
2020-09-11 14:35:47 +00:00
" @Keymaker: Go ahead, try it. Type anything to @Hasher. " ,
( ' str_to_hash ' , f ' @ { name } : ' , input )
)
str_to_hash = res . get ( ' vals ' ) . get ( ' str_to_hash ' )
2020-09-11 17:17:21 +00:00
hashed_str1 = hasher ( str_to_hash . encode ( ) )
2020-09-11 14:35:47 +00:00
res = self . status (
2020-09-11 17:17:21 +00:00
' @Hasher: ' + hashed_str1
2020-09-11 14:35:47 +00:00
)
res = self . status (
2020-09-11 17:17:21 +00:00
' @Keymaker: Whatever you typed, there \' s no way to reconstruct it from that garbled mess. ' ,
' But whatever you typed will always produce the *same* garbled mess. ' ,
( ' str_to_hash ' , f ' Try typing the exact same thing over again: \n @ { name } : ' , input )
2020-09-11 14:35:47 +00:00
)
str_to_hash = res . get ( ' vals ' ) . get ( ' str_to_hash ' )
2020-09-11 17:17:21 +00:00
hashed_str2 = hasher ( str_to_hash . encode ( ) )
2020-09-11 14:35:47 +00:00
res = self . status (
2020-09-11 17:17:21 +00:00
' @Hasher: ' + hashed_str2
2020-09-11 14:35:47 +00:00
)
2020-09-11 17:17:21 +00:00
if hashed_str1 == hashed_str2 :
self . status ( ' See how the hashed values are also exactly the same? ' )
else :
self . status ( ' See how the hashed values have also changed? ' )
2020-09-11 14:35:47 +00:00
res = self . status (
2020-09-11 17:17:21 +00:00
( ' str_to_hash ' , f ' Now try typing something just a little bit different: \n @ { name } : ' , input )
2020-09-11 14:35:47 +00:00
)
str_to_hash = res . get ( ' vals ' ) . get ( ' str_to_hash ' )
2020-09-11 17:17:21 +00:00
hashed_str3 = hasher ( str_to_hash . encode ( ) )
2020-09-11 14:35:47 +00:00
res = self . status (
2020-09-11 17:17:21 +00:00
' @Hasher: ' + hashed_str3
2020-09-11 14:35:47 +00:00
)
2020-09-11 17:17:21 +00:00
if hashed_str2 == hashed_str3 :
self . status ( ' See how the hashed values are also the same? ' )
2020-09-11 14:35:47 +00:00
else :
2020-09-11 17:17:21 +00:00
self . status ( ' See how the hashed values have also changed? ' )
2020-09-11 14:35:47 +00:00
2020-09-11 17:17:21 +00:00
self . status (
None , { indent_str ( ART_FROG_BLENDER , 10 ) } ,
' @Keymaker: Behind the scenes, @Hasher is using the SHA-256 hashing function, which was designed by the NSA. ' ,
' But @Hasher also adds a secret " salt " to the recipe, as it \' s called. ' ,
' To whatever you type in, @Hasher adds a secret phrase: another random string of characters which never changes. ' ,
" By doing so, the hash output is \" salted \" : made even more idiosyncratic to outside observers. " ,
)
self . status (
None , { indent_str ( ART_FROG_BLENDER , 10 ) } ,
f " I ' ve taken the liberty of generating a random secret for your device, which I show here mostly redacted: " ,
make_key_discreet_str ( persona . crypt_keys . secret . decode ( ) , 0.25 ) ,
' The full version of this secret is silently added to every input you type into @Hasher. ' ,
" I ' ve saved this secret phrase to a hidden location on your device hardware. " ,
)
self . status (
None , { indent_str ( ART_FROG_BLENDER , 10 ) } ,
' However, this means that you will be unable to log in to your account from any other device. ' ,
' This limitation provides yet another level of hardware protection against network attacks. ' ,
' However, you can always choose (not recommended) to the secret file with another device by a secure channel. ' ,
3 , f ' But, please excuse me Komrade @ { name } -- I digress. '
)
while not passphrase :
res = self . status ( None ,
{ indent_str ( ART_FROG_BLENDER , 10 ) } ,
" @Keymaker: Please type your chosen password into @Hasher. " ,
( ' str_to_hash ' , f ' \n @ { name } : ' , getpass ) ,
pause = False
)
str_to_hash = res . get ( ' vals ' ) . get ( ' str_to_hash ' )
hashed_pass1 = hasher ( str_to_hash . encode ( ) )
res = self . status (
' \n @Hasher: ' + hashed_pass1 ,
pause = False
)
res = self . status (
' \n Now type in the same password one more time to verify it: ' ,
( ' str_to_hash ' , f ' \n @ { name } : ' , getpass ) ,
pause = False
)
str_to_hash = res . get ( ' vals ' ) . get ( ' str_to_hash ' )
hashed_pass2 = hasher ( str_to_hash . encode ( ) )
res = self . status (
' \n @Hasher: ' + hashed_pass2 ,
pause = False
)
if hashed_pass1 == hashed_pass2 :
self . status ( ' ' , ' @Keymaker: Excellent. The passwords clearly matched, because the hashed values matched. ' , pause = False )
passphrase = hashed_pass1
else :
self . status ( ' @Keymaker: A pity. It looks like the passwords didn \' t match, since the hashed values didn \' t match either. Try again? ' )
return passphrase
def status_keymaker_part3 ( self , privkey , privkey_decr , privkey_encr , passphrase ) :
2020-09-12 07:55:23 +00:00
self . status (
None , { tw . indent ( ART_KEY , ' ' * 5 ) + ' \n ' , True } ,
# None,{ART_+'\n',True},
' Now that we have a hashed passphrase, we can generate the (2A) encryption key. ' ,
{ ART_KEY_KEY2A , True , 0.1 } ,
''' The key is formed using Themis ' s high-level symmetric encryption library: SecureCell, using Seal mode. ''' ,
' This key (2A) then uses the AES-256 encryption algorithm to encrypt the super-sensitive private key (2): '
)
2020-09-11 17:17:21 +00:00
2020-09-12 07:55:23 +00:00
s0 = str . center ( ' [Encryption Process] ' , CLI_WIDTH )
2020-09-12 09:25:09 +00:00
s1 = s0 + ' \n \n ' + self . printt ( ' Now that we have (2A), we can use it to encrypt the super-sensitive private key (2): ' , ret = True )
s2a = self . printt ( f " (2A) { make_key_discreet_str ( passphrase ) } " , ret = True )
s2 = self . printt ( f " (2) { make_key_discreet ( privkey . data_b64 ) } " , ret = True )
s2b = self . printt ( f " (2B) { make_key_discreet ( b64encode ( privkey_encr ) ) } " , ret = True )
2020-09-12 07:55:23 +00:00
self . status (
# screen 1
None , { f ' { s1 } ' } ,
False ,
# 2
None , { f ' { s1 } \n \n { ART_KEY_PAIR_SPLITTING1 } ' } ,
{ s2a , True } ,
False ,
# 3
None , { f ' { s1 } \n \n { ART_KEY_PAIR_SPLITTING2 } \n { s2a } ' } ,
{ ' \n ' + s2 , True } ,
False ,
# 4
None , { f ' { s1 } \n \n { ART_KEY_PAIR_SPLITTING3 } \n { s2a } \n \n { s2 } ' } ,
{ ' \n ' + s2b , True } ,
False ,
)
2020-09-11 14:35:47 +00:00
2020-09-12 07:55:23 +00:00
2020-09-12 09:25:09 +00:00
shdr = str . center ( ' [Decryption Process] ' , CLI_WIDTH ) + ' \n \n ' + self . printt ( ' Once we have (2B), we don \' t need (2A) or (2) anymore. We can regenerate them! ' , ret = True )
2020-09-12 07:55:23 +00:00
from getpass import getpass
passhash = None
2020-09-11 14:35:47 +00:00
2020-09-12 07:55:23 +00:00
while passhash != passphrase :
res = self . status (
None , { shdr } , False if passhash is None else True ,
2020-09-11 14:35:47 +00:00
2020-09-12 09:25:09 +00:00
( " pass " , self . printt ( f " Let ' s try. Re-type your password into @Hasher: " , ret = True ) + f " \n " , getpass )
2020-09-12 07:55:23 +00:00
)
passhash = self . persona . crypt_keys . hash ( res . get ( ' vals ' ) . get ( ' pass ' ) . encode ( ) )
if passhash != passphrase :
self . status ( { ' Looks like they don \' t match. Try again? ' } , False )
2020-09-11 14:35:47 +00:00
2020-09-12 07:55:23 +00:00
self . status (
{ ' Excellent. We can now regenerate the decryption key: ' } , False ,
{ s2a , True } , False ,
)
2020-09-11 14:35:47 +00:00
2020-09-12 07:55:23 +00:00
# self.status('great')
# shdr2=
self . status (
# 2
None , { f ' { shdr } \n \n { ART_KEY_PAIR_SPLITTING1 } ' } ,
{ s2a , True } ,
False ,
# 3
# None,{f'{s1}\n\n{ART_KEY_PAIR_SPLITTING2}\n{s2a}'},
# {'\n'+s2,True},
# False,
# 4
None , { f ' { s1 } \n \n { ART_KEY_PAIR_SPLITTING4 } \n { s2a } \n \n \n \n ' } ,
{ ' \n ' + s2b , True } ,
False ,
)
2020-09-11 14:35:47 +00:00
2020-09-10 21:32:59 +00:00
2020-09-14 20:10:31 +00:00
def run_cli ( inp ) :
2020-09-10 21:32:59 +00:00
cli = CLI ( )
2020-09-16 10:23:18 +00:00
cli . run ( inp ) #'/signup elon') #'/signup',name='elon')
2020-09-10 21:32:59 +00:00
if __name__ == ' __main__ ' :
2020-09-14 20:10:31 +00:00
inp = ' ' . join ( sys . argv [ 1 : ] )
run_cli ( inp )
2020-09-12 07:55:23 +00:00
# asyncio.run(test_async())
"""
Outtakes
self . status ( None ,
{ ART_KEY_PAIR31A } ,
{ ART_KEY_PAIR3B + ' \n ' , True } ,
3 , ' Allow me to explain. ' ,
' (2A) is a separate encryption key generated by your password. ' ,
' (2B) is a version of (2) which has been encrypted by (2A). ' ,
" Because (2) will be destroyed, to rebuild it requires decrypting (2B) with (2A). " ,
)
self . status (
None , { ART_KEY_PAIR5 + ' \n ' } ,
" However, in a final move, I will now destroy (2A), too. " ,
None , { ART_KEY_PAIR4Z1 + ' \n ' } ,
' Why? Because now only you can regenerate it by remembering the password which created it. ' ,
# None,{ART_KEY_PAIR4Z1+'\n'},
' However, this also means that if you lose or forget your password, you \' re screwed. ' ,
None , { ART_KEY_PAIR4Z2 + ' \n ' } ,
" Because without key (2A),you couldn never unlock (2B). " ,
None , { ART_KEY_PAIR4Z3 + ' \n ' } ,
" And without (2B) and (2A) together, you could never re-assemble the private key of (2). " ,
None , { ART_KEY_PAIR4Z42 + ' \n ' } ,
" And without (2), you couldn ' t read messages sent to your public key. " ,
)
"""