diff --git a/komrade/backend/callers.py b/komrade/backend/callers.py index eaaa24b..a97f61e 100644 --- a/komrade/backend/callers.py +++ b/komrade/backend/callers.py @@ -1,6 +1,7 @@ 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 * +from komrade.cli import * # from komrade.backend.the_telephone import * # from komrade.backend.the_telephone import * @@ -18,3 +19,8 @@ class Caller(Operator): to_whom=self.op, get_resp_from=self.phone.ring_ring ) + + + + # @hack: repurposing this for now as a narrator +e \ No newline at end of file diff --git a/komrade/backend/keymaker.py b/komrade/backend/keymaker.py index 2760e6d..b594afe 100644 --- a/komrade/backend/keymaker.py +++ b/komrade/backend/keymaker.py @@ -2,6 +2,15 @@ import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.p from komrade import * from komrade.backend.crypt import * from abc import ABC, abstractmethod + +# common external imports +from pythemis.skeygen import KEY_PAIR_TYPE, GenerateKeyPair +from pythemis.smessage import SMessage, ssign, sverify +from pythemis.skeygen import GenerateSymmetricKey +from pythemis.scell import SCellSeal +from pythemis.exception import ThemisError + + class KomradeKey(ABC,Logger): @abstractmethod @@ -14,7 +23,8 @@ class KomradeKey(ABC,Logger): def data_b64(self):return b64encode(self.data) @property def discreet(self): return make_key_discreet(self.data) - + def __str__(self): + return repr(self) class KomradeSymmetricKey(KomradeKey): @@ -32,6 +42,7 @@ class KomradeSymmetricKey(KomradeKey): def decrypt(self,msg,**kwargs): return self.cell.decrypt(msg,**kwargs) + def getpass_status(passphrase=None): while not passphrase: passphrase1 = getpass(f'@Keymaker: What is a *memorable* pass word or phrase? Do not write it down.\n@{name}: ') @@ -41,13 +52,14 @@ def getpass_status(passphrase=None): else: return passphrase1 +get_pass_func = getpass_status if SHOW_STATUS else getpass class KomradeSymmetricKeyWithPassphrase(KomradeSymmetricKey): def __init__(self,passphrase=DEBUG_DEFAULT_PASSPHRASE, why=WHY_MSG): self.passphrase=passphrase if not self.passphrase: - self.passphrase=getpass_status if SHOW_LOG else getpass.getpass(why) + self.passphrase=getpass(why) #return self.passphrase @property def data(self): return KEY_TYPE_SYMMETRIC_WITH_PASSPHRASE.encode('utf-8') @@ -63,9 +75,16 @@ class KomradeSymmetricKeyWithoutPassphrase(KomradeSymmetricKey): class KomradeAsymmetricKey(KomradeKey): - def __init__(self,pubkey,privkey): + def __init__(self,pubkey=None,privkey=None): + if not pubkey or not privkey: + keypair = GenerateKeyPair(KEY_PAIR_TYPE.EC) + privkey = keypair.export_private_key() + pubkey = keypair.export_public_key() self.pubkey=pubkey - self.privkey=privkey + self.privkey=privkey + self.privkey_obj = KomradeAsymmetricPrivateKey(privkey,pubkey) + self.pubkey_obj = KomradeAsymmetricPublicKey(pubkey,privkey) + def encrypt(self,msg,pubkey=None,privkey=None): if issubclass(type(msg), KomradeKey): msg=msg.data pubkey=pubkey if pubkey else self.pubkey @@ -107,6 +126,11 @@ def make_key_discreet(data,chance_bowdlerize=0.5): return ''.join((k if random.random()\n{_key_encr}') + # keychain[key_name]=_key_encr keychain[key_name]=_key_encr_obj return keychain @@ -356,52 +400,68 @@ class Keymaker(Logger): keys_to_save = KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_SERVER, keys_to_return = KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_CLIENT, keys_to_gen = KEYMAKER_DEFAULT_KEYS_TO_GEN, - key_types = KEYMAKER_DEFAULT_KEY_TYPES): + key_types = KEYMAKER_DEFAULT_KEY_TYPES, + save_keychain=True, + return_keychain=True, + return_all_keys=False): # setup keys_to_gen = set(keys_to_gen) | set(keys_to_save) | set(keys_to_return) keys_to_gen = sorted(list(keys_to_gen),key=lambda x: x.count('_')) key_types = dict([(k,key_types[k]) for k in keys_to_gen]) if not name: name=self.name - print('forging!') + # show user what's happening self.log(f''' Keymaker ({self}) is forging new keys for {name} - I will save these keys in this crypt: - {keys_to_save} - I will also save this user's pubkey (as b64 URI) to: - {self.get_path_qrcode(name=name)} - I will return these keys to you: - {keys_to_return} - which means I will end up generating these keys: - {keys_to_gen} - I will also be using these key types to do so: - {dict_format(key_types,tab=4)} - ''') +''' + (''' +* I will save these keys in this crypt: {', '.join(keys_to_save)} +''' if save_keychain else '') #+ #''' +# * I will also save this user's pubkey (as b64 URI) to: + # {self.get_path_qrcode(name=name)} +# ''' + (f''' ++ (f''' +* I will return these keys to you: {', '.join(keys_to_return)} +''' if return_keychain else '') ++ f''' +* I will forge these keys for you: {', '.join(keys_to_gen)} + +* I will be using these key types to do so: + {dict_format(key_types,tab=4)} +''') # gen decryptor keys! keychain = self.gen_keys_from_types(key_types,passphrase=passphrase) # gen encrypted keys! + # self.log('I built this keychain v1!',dict_format(keychain,tab=2)) + keychain = self.gen_encr_keys(keychain,keys_to_gen,passphrase=passphrase) - self.log('I built this keychain!',dict_format(keychain,tab=2)) + # self.log('I built this keychain!',dict_format(keychain,tab=2)) # self.status('@Keymaker: I ended up building these keys:',keychain) + # save keys! - # get URI id to save under (except for pubkeys, accessible by name) - uri_id,keys_saved_d,keychain = self.save_keychain(name,keychain,keys_to_save) - self.log('I saved this keychain:',dict_format(keys_saved_d,tab=2),'using the generated-from-pubkey URI ID',uri_id) + if save_keychain: + # get URI id to save under (except for pubkeys, accessible by name) + uri_id,keys_saved_d,keychain = self.save_keychain(name,keychain,keys_to_save) + # self.log('I saved this keychain:',dict_format(keys_saved_d,tab=2),'using the generated-from-pubkey URI ID',uri_id) # return keys! - keys_returned = self.return_keychain(keychain,keys_to_return) - self.log('I am returning this keychain:',dict_format(keys_returned,tab=2)) + if return_all_keys: + return keychain + if return_keychain: + keys_returned = self.return_keychain(keychain,keys_to_return) + # self.log('I am returning this keychain:',dict_format(keys_returned,tab=2)) + # return (uri_id,keys_returned) + return keys_returned - print('done forging!') + - return (uri_id,keys_returned) + raise KomradeException('What did you want me to do here?') def return_keychain(self,keychain,keys_to_return=None): @@ -422,11 +482,9 @@ Keymaker ({self}) is forging new keys for {name} def qr(self): return self.qr_str(data=self.uri_id) def qr_str(self,data=None): - import qrcode - qr=qrcode.QRCode() - qr.add_data(self.uri_id if not data else data) - ascii = capture_stdout(qr.print_ascii) - return ascii + data = self.uri_id if not data else data + return get_qr_str(data) + def save_uri_as_qrcode(self,uri_id=None,name=None): if not uri_id: uri_id = self.uri_id @@ -458,7 +516,7 @@ Keymaker ({self}) is forging new keys for {name} keys_saved_d={} for keyname in keys_to_save: if not '_' in keyname and keyname!='pubkey': - raise KomradeException('there is no private property in a socialist network! all keys must be split between komrades') + self.log('there is no private property in a socialist network! all keys must be split between komrades',keyname) if keyname in keychain: # uri = uri_id uri = uri_id if keyname!='pubkey' else name @@ -533,7 +591,7 @@ Keymaker ({self}) is forging new keys for {name} # self.log('assembled_key built:',key) return key except ThemisError as e: - # self.log('!! decryption failed:',e) + self.log('!! decryption failed:',e) return def get_cell(self, str_or_key_or_cell): diff --git a/komrade/backend/people.py b/komrade/backend/people.py index ac37812..c18e0bf 100644 --- a/komrade/backend/people.py +++ b/komrade/backend/people.py @@ -1,12 +1,15 @@ 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 * - +from komrade.backend.keymaker import * class Persona(Caller): def __init__(self, name=None, passphrase=DEBUG_DEFAULT_PASSPHRASE): super().__init__(name=name,passphrase=passphrase) + if SHOW_STATUS: + from komrade.cli import CLI + self.cli = CLI() # self.boot(create=False) # def boot(self,create=False): @@ -42,22 +45,38 @@ class Persona(Caller): # def login(self): # if keys.get('pubkey') and keys.get('privkey') - def register(self, name = None, passphrase = None, is_group=None): + def register(self, name = None, passphrase = None, is_group=None, show_intro=0,show_body=True): # defaults if name and not self.name: self.name=name if not name and self.name: name=self.name if not name and not self.name: name='' + clear_screen() + + # intro narration? + if SHOW_STATUS and show_intro: + name = self.cli.status_keymaker_intro(name) + + + + # forge public/private keys + keypair = KomradeAsymmetricKey() + pubkey,privkey = keypair.pubkey_obj,keypair.privkey_obj + + # make sure we have passphrase + if SHOW_STATUS: + passphrase = self.cli.status_keymaker_body( + name, + passphrase, + pubkey, + privkey, + self.crypt_keys.hash + ) + else: + if not passphrase: passphrase=getpass('Enter a memorable password to encrypt your private key with: ') - if not passphrase: passphrase=getpass('Enter a memorable password: ') + # encrypt private key + exit() - # make and save keys locally - uri_id,keys_returned = self.forge_new_keys( - name=name, - passphrase=passphrase, - keys_to_save = KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_CLIENT, - keys_to_return = KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_SERVER - ) - self.log(f'my new uri is {uri_id} and I got new keys!: {dict_format(keys_returned)}') # save the ones we should on server data = { @@ -96,15 +115,16 @@ def test_register(): marxbot.register() if __name__=='__main__': - marx = Persona('marx') - elon = Persona('elon') + test_register() + # marx = Persona('marx') + # elon = Persona('elon') - marx.register() - # elon.register() - # person.register() - # print(person.pubkey) + # marx.register() + # # elon.register() + # # person.register() + # # print(person.pubkey) - # elon.send_msg_to('youre dumb',marx) - #Caller('elon').ring_ring({'_route':'say_hello','_msg':'my dumb message to operator'}) + # # elon.send_msg_to('youre dumb',marx) + # #Caller('elon').ring_ring({'_route':'say_hello','_msg':'my dumb message to operator'}) - # print(marx.exists_on_server()) \ No newline at end of file + # # print(marx.exists_on_server()) \ No newline at end of file diff --git a/komrade/cli/__init__.py b/komrade/cli/__init__.py index e69de29..4f665b2 100644 --- a/komrade/cli/__init__.py +++ b/komrade/cli/__init__.py @@ -0,0 +1,2 @@ +from .artcode import * +from .cli import * \ No newline at end of file diff --git a/komrade/cli/artcode.py b/komrade/cli/artcode.py index cc71176..7419083 100644 --- a/komrade/cli/artcode.py +++ b/komrade/cli/artcode.py @@ -2,19 +2,19 @@ ART_TELEPHONE = ''' ..--""""----.. .-" ..--""""--.j-. .-" .-" .--.""--.. - .-" .-" ..--"-. \/ ; + .-" .-" ..--"-. \\/ ; .-" .-"_.--..--"" ..--' "-. : - .' .' / `. \..--"" __ _ \ ; - :.__.-" \ / .' ( )"-. Y - ; ;: ( ) ( ). \ - .': /:: : \ \ - .'.-"\._ _.-" ; ; ( ) .-. ( ) \ - " `.""" .j" : : \ ; ; \ - bug /"""""/ ; ( ) "" :.( ) \ - /\ / : \ \`.: _ \ - : `. / ; `( ) (\/ :" \ \ - \ `. : "-.(_)_.' t-' ; - \ `. ; ..--": + .' .' / `. \\..--"" __ _ \\ ; + :.__.-" \\ / .' ( )"-. Y + ; ;: ( ) ( ). \\ + .': /:: : \\ \\ + .'.-"\\._ _.-" ; ; ( ) .-. ( ) \\ + " `.""" .j" : : \\ ; ; \\ + bug /"""""/ ; ( ) "" :.( ) \\ + /\\ / : \\ \\`.: _ \\ + : `. / ; `( ) (\\/ :" \\ \\ + \\ `. : "-.(_)_.' t-' ; + \\ `. ; ..--": `. `. : ..--"" : `. "-. ; ..--"" ; `. "-.:_..--"" ..--" @@ -25,30 +25,30 @@ ART_TELEPHONE = ''' ''' ART_PHONE_SM1 = """ .----------------. - / _H______H_ \@, - \____/ \____/ @, - / \ `@ + / _H______H_ \\@, + \\____/ \\____/ @, + / \\ `@ | LI LI LI | ,@ | LI LI LI | ,@' | LI LI LI | ,@' | LI LI LI |@@' -jgs \ /' +jgs \\ /' `----------' """ ART_ROTARY2=""" _______________ - / \ + / \\ | .---------. |@ '---' .-----. '---'@ - .' /6 5_4 3\ '. @ - | |7 /...\ 2| | @ - | |8 \___/ 1| | @ - | \_9_0_)\/ | @@ + .' /6 5_4 3\\ '. @ + | |7 /...\\ 2| | @ + | |8 \\___/ 1| | @ + | \\_9_0_)\\/ | @@ /==|_____________|@@@@ H-------------------@@ H ) || || ( @@ - H / || || \ @ + H / || || \\ @ H |----''---''----| =/ |_______________| """ @@ -64,16 +64,16 @@ ART_KEY = """ ART_OLDPHONE = """ __ - /` _`\ + /` _`\\ | (_()| .-. - \_ _/_/ \ + \\_ _/_/ \\ ||=[_] | || | | | - ||/ \ | + ||/ \\ | ||`---' / .--'||-.___.' /` .-||-. - '-/`.____.`\ + '-/`.____.`\\ jgs '.______.' """ @@ -81,13 +81,13 @@ ART_OLDPHONE2=""" _|~|/| ( | | | - /_|_|\| + /_|_|\\| | | | |~| | | | | | | | |-| - | | \ + | | \\ | |__| |_|_ / ~-_ @@ -101,16 +101,16 @@ ART_ROTARY = """ ,-' ,-. `-. ,' ,-. ( 4 ) ,-. `. ,' ( 5 ) `-' ( 3 ) `. - / ,-. `-',-'' ``-.`-' ,-. \ - / ( 6 ) ,' `. ( 2 ) \ -: `-' / FEUER \ `-' : + / ,-. `-',-'' ``-.`-' ,-. \\ + / ( 6 ) ,' `. ( 2 ) \\ +: `-' / FEUER \\ `-' : | ,-. : ________ : ,-. | |( 7 ) | |________| | ( 1 )| | `-' : ; `-' | -: ,-. \ NOTRUF / ; - \ ( 8 ) `. ,'(`. / - \ `-' ,-.`-..__..-' \ `-./ - `. ( 9 ) ,-. \ ,' +: ,-. \\ NOTRUF / ; + \\ ( 8 ) `. ,'(`. / + \\ `-' ,-.`-..__..-' \\ `-./ + `. ( 9 ) ,-. \\ ,' `. `-' ( 0 ) ,'` `-._ `-' _.-' ```----''' SSt @@ -124,17 +124,17 @@ ART_PHONE_DIAGRAM = """ / `--' / | /__ __ __ / | //_//_//_// / __ - //_//_//_// / \`.___ Listening end + //_//_//_// / \\`.___ Listening end //_//_//_// / //_//_//_// /__ - / / / \`.___ Buttons + / / / \\`.___ Buttons / .-. / / / /#/ / / / `-' / /__ - / .====. / / \`.___ Speaking end + / .====. / / \\`.___ Speaking end |`--------' / - \ , .'__ - `-//----' \`.___ Disconnect button + \\ , .'__ + `-//----' \\`.___ Disconnect button // """ @@ -142,14 +142,14 @@ ART_OLDPHONE3 = """ __ _ .: .' '. - /: / \_ - ;: ; ,-'/`:\ + /: / \\_ + ;: ; ,-'/`:\\ |: | | |():| - ;: ; '-.\_:/ - \: \ /` + ;: ; '-.\\_:/ + \\: \\ /` ':_'._.' || - /__\ + /__\\ .---. {====} .' _,"-,__|:: | / ((O)=;--.:: | @@ -159,13 +159,13 @@ ART_OLDPHONE3 = """ | |: | |:: | | |: | |:: | | |: | |:: | - | /:'__\ |:: | + | /:'__\\ |:: | | [______]|:: | | `----` |:: |__ | _.--|:: | ''--._ ; .' __{====}__ '. - \ .'_.-'._ `""` _.'-._ '. - '--'/` `''''` `\ '.__ + \\ .'_.-'._ `""` _.'-._ '. + '--'/` `''''` `\\ '.__ jgs '._ _.' `""--......--""` @@ -174,44 +174,561 @@ ART_OLDPHONE3 = """ ART_OLDPHONE4 = """ __ - /` _`\ + /` _`\\ | (_()| .-. - \_ _/_/ \ + \\_ _/_/ \\ ||=[_] | || | | | - ||/ \ | + ||/ \\ | ||`---' / .--'||-.___.' /` .-||-. - '-/`.____.`\ - jgs '.______.' - + '-/`.____.`\\ + '.______.' """ +# by jgs ART_PAYPHONE = """ _________________ - / __ \ + / __ \\ | (__) | | | | .-----. .--. | - | | | / \ | - | '-----' \ / | + | | | / \\ | + | '-----' \\ / | | | | | | LI LI LI | | | | LI LI LI | | |Oo | LI LI LI | | |`Oo | LI LI LI | | | Oo | | | | Oo - | .------. / \ | oO - | | | \ / | Oo + | .------. / \\ | oO + | | | \\ / | Oo | '------' '-oO | oO | .---Oo | Oo | || ||`Oo oO | |'--'| | OoO | '----' | -jgs \_________________/ +jgs \\_________________/ +""" + +ART_HAMMER = """ + , + /( ___________ + | >:===========` + )( + "" +""" + + + +ART_KEY_PAIR = """ + + __ + /o \\_____ + \__/-="="` + __ + / o\\ + \_ / + <| + <| + <| + ` +""" + + +ART_KEY_PAIR2 = """ + + __ + /o \\_____ + \__/-="="` + __ (public) + / o\\ + \_ / + <| + <| + <| (private) + +""" + + +ART_KEY_PAIR2A = """ + + __ + /o \\_____ + \__/-="="` + __ (1) public key + / o\\ + \_ / + <| + <| + <| + +""" + + +ART_KEY_PAIR2B = """ + + __ + /o \\_____ + \__/-="="` + __ (1) public key + / o\\ + \_ / + <| + <| + <| (2) private key + +""" + + + + + +ART_KEY_PAIR32 = """ + + __ + /o \\_____ + \__/-="="` + _ _ (1) public key + /o \\ + \ _ / + < | + < | + < | (2B) privkey_encr + + (2A) privkey_decr + +""" + + +ART_KEY_PAIR3A2 = """ + + __ + /o \\_____ + \__/-="="` + _ _ (1) public key + /o \\ + \ _ / + < | + < | + < | + + + +""" + + +ART_KEY_PAIR3B2 = """ + + __ + /o \\_____ + \__/-="="` + _ _ + /o \\ + \ _ / + < | + < | + < | (2B) privkey_encr + + + +""" + + +ART_KEY_PAIR3C2 = """ + + __ + /o \\_____ + \__/-="="` + _ _ (1) public key + /o \\ + \ _ / + < | + < | + < | (2B) privkey_encr + + (2A) privkey_decr + +""" + + +ART_KEY_PAIR3 = """ + + __ + /o \\_____ + \__/-="="` + _ _ (1) public key + /o \\ + \ _ / + < | + < | + < | (2B) privkey_encr + + (2A) privkey_decr + +""" + +ART_KEY_PAIR4 = """ + + __ + /o \\_____ + \__/-="="` + ? _ + ?? \\ + ? _ / + ? | + ? | + ? | (2B) privkey_encr + + (2A) privkey_decr + +""" + +ART_KEY_PAIR4Z = """ + + __ + /o \\_____ + \__/-="="` + ? _ + ?? \\ + ? _ / + ? | + ? | + ? | + + (2A) privkey_decr + +""" + + +ART_KEY_PAIR4D = """ + ? _ + ?? \\ + ? _ / + ? | + ? | + ? | (2B) privkey_encr + + (2A) privkey_decr + +""" + +ART_KEY_PAIR4C = """ + ? _ + ?? \\ + ? _ / + ? | + ? | + ? | + + (2A) privkey_decr + +""" + + +ART_KEY_PAIR4B = """ ? _ + ?? \\ + ? _ / + ? | + ? | + ? | + +""" + +ART_KEY_PAIR31A = """ + + __ + /o \\_____ + \__/-="="` + (1) public key""" + + +ART_KEY_PAIR3A = """ + + __ + /o \\_____ + \__/-="="`""" +ART_KEY_PAIR3B = """ _ _ + /o \\ + \ _ / + < | + < | + < | +(2A) (2B) encrypted + encryption form of (2) + key for (2B) + +""" + +ART_KEY_PAIR5 = """ + + __ + /o \\_____ + \__/-="="` + (1) public key + _ _ + /o \\ + \ _ / + < | + < | + < | +(2A) (2B) encrypted + encryption form of (2) + key for (2B) +""" + + +ART_KEY_PAIR4Z1 = """ + + __ + /o \\_____ + \__/-="="` + (1) public key + ? _ + ?? \\ + ? _ / + ? | + ? | + ? | + (2B) encrypted + form of (2) + +""" +ART_KEY_PAIR4Z2 = """ + + __ + /o \\_____ + \__/-="="` + (1) public key + ? ? + ?? ? + ? ? ? + ? ? + ? ? + ? ? + + + +""" +# """ +ART_KEY_PAIR4Z3 = """ + + __ + /o \\_____ + \__/-="="` + ?? (1) public key + ? ? + ? ? + ?? + ?? + ?? + + + +""" +ART_KEY_PAIR4Z3 = """ + + __ + /o \\_____ + \__/-="="` + ?? (1) public key + ? ?? + ? ? + ?? + ? + ?? + + + +""" + +ART_KEY_PAIR4Z42 = """ + + ? + ? ?????? + ?? ? ? ?? + ?? + ? ?? + ? ? + ?? + ? + ?? + + + +""" + + +ART_KEY_PAIR4Z4B = """ + + ?? + ?? ?????? + ?????????? + __ + / o\\ + \_ / + <| + <| + <| + + + +""" + +ART_KEY_PAIR4Z4 = """ + + ?? + ?? ?????? + ?????????? + __ + / o\\ + \_ / + <| + <| + <| + + + +""" + + +ART_KEY_PAIR3BB = """ ? _ + ?? \\ + ? _ / + ? | + ? | + ? | +(2A) (2B) encrypted + encryption form of (2) + key for (2B) + +""" + + + + +ART_KEY_PAIR3AA = """ _ _ + /o \\ + \ _ / + < | + < | + < | + """ + + + +ART_KEY_PAIR_SEP = """ + + __ + /o \\_____ + \__/-="="` + __ + / o\\ + \_ / + <| + <| + <| + ` +""" + + + +ART_KEY_CHAIN = """ + ___________ @ @ + / (@\\ @ + \___________/ _@ + @ _/@ \\_____ + @/ \__/-="="` + \_ / + <| + <| + <| + ` +""" + + +ART_FROG_BLENDER =""" + ___ + _______|___|______ +__|__________________| +\ ]________________[ `---. + `. ___ L + | _ | L | + | .'_`--.___ __ | | | + |( 'o` - .`.'_ ) | F F + | `-._ `_`./_ | / / + J '/\\ ( .'/ )F.' / + L ,__//`---'`-'_/J .' + J /-' '/ F.' + L ' J' + J `.`-. .-'.' F + L `.-'.-' J + |__(__(___)__| + F J + J L + |______________| + +""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -264,7 +781,7 @@ def convert_image_to_ascii(image, new_width=100): image_ascii = [pixels_to_chars[index: index + new_width] for index in range(0, len_pixels_to_chars, new_width)] - return "\n".join(image_ascii) + return "\\n".join(image_ascii) def handle_image_conversion(image_filepath): image = None diff --git a/komrade/cli/cli.py b/komrade/cli/cli.py index de18e3b..a156a7d 100644 --- a/komrade/cli/cli.py +++ b/komrade/cli/cli.py @@ -2,6 +2,7 @@ import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.p from komrade import * from komrade.backend import * import art +import textwrap as tw @@ -16,8 +17,8 @@ class CLI(Logger): self.name='' self.cmd='' - async def run(self,inp,name=''): - if name: self.name=name + def run(self,inp='',name=''): + self.name=name clear_screen() self.boot() self.help() @@ -26,11 +27,11 @@ class CLI(Logger): while True: try: - inp=input() + inp=input(f'@{self.name if self.name else "?"}: ') except KeyboardInterrupt: exit() self.route(inp) - await asyncio.sleep(0.5) + #await asyncio.sleep(0.5) def route(self,inp): inp=inp.strip() @@ -42,37 +43,42 @@ class CLI(Logger): f=getattr(self,cmd) return f(dat) - def boot(self): - print(art.text2art(CLI_TITLE,font=CLI_FONT)) + def boot(self,indent=5): + logo=art.text2art(CLI_TITLE,font=CLI_FONT) + # logo=make_key_discreet_str(logo,chance_bowdlerize=0.1) #.decode() + logo=tw.indent(logo, ' '*indent) + scan_print(logo,max_pause=0.005) def help(self): print() for cmd,info in self.ROUTES.items(): print(f' /{cmd}: {info}') + # print('\n') print('\n') def intro(self): self.status(None,) - ## routes - def register(self,name=None): - # defaults - if name and not self.name: self.name=name - if not name and self.name: name=self.name - if not name and not self.name: name='' - - # self.status(None,ART_PAYPHONE,3,pause=False) #,ticks = None) + def register(self,dat): + self.persona = Persona(self.name) + self.persona.register() + + + + + ### DIALOGUES - # hello, op? + # hello, op? + def status_keymaker_intro(self,name): + self.status(None,{ART_OLDPHONE4+'\n',True},3) #,scan=False,width=None,pause=None,clear=None) + nm=name if name else '?' self.status( - f'\n\n@{nm}: Uh yes hello, Operator? I would like to join Komrade, the socialist network. Could you patch me through?',clear=False) + 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) while not name: name=self.status(('name','@TheTelephone: Of course, Komrade...?\n@')).get('vals').get('name').strip() print() - self.name = name - self.persona = Persona(name) self.status( f'@TheTelephone: Of course, Komrade @{name}. A fine name.', @@ -81,7 +87,7 @@ class CLI(Logger): '''Komrade @TheOperator lives on the deep web. She's the one you want to speak with.''', - f'''@{name}: Hm, ok. Well, could you patch me through to the remote operator then?''', + None,{ART_OLDPHONE4},f'''@{name}: Hm, ok. Well, could you patch me through to the remote operator then?''', f'''@{TELEPHONE_NAME}: I could, but it's not safe yet. Your information could be exposed. You need to forge your encryption keys first.''', @@ -91,64 +97,159 @@ class CLI(Logger): clear=False,pause=True) - - - ### KEYMAKER - self.status(None,ART_KEY,3,pause=False,clear=False) + self.status(None,{tw.indent(ART_KEY,' '*5)+'\n',True},3) #,clear=False,indent=10,pause=False) # convo - self.status('', - f'@{name}: Hello, Komrade @Keymaker? I would like help forging a new set of keys.', + self.status( + f'\n@{name}: Hello, Komrade @Keymaker? I would like help forging a new set of keys.', f'@Keymaker: Of course, Komrade @{name}.', + ) - '''We will make three keys. First, a matching, "asymmetric" pair.''', + return name - '\t1) A "public key" you can share with anyone.', - - '\t2) A "private key" other no one can ever, ever see.', - 'With both together, you can communicate privately and securely with anyone who also has their own key pair.', - 'We will use the use the iron-clad Elliptic Curve algorthm to generate the keypair, accessed via a high-level cryptography library, Themis (https://github.com/cossacklabs/themis).', + def status_keymaker_body(self,name,passphrase,pubkey,privkey,hasher): + # 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( + # 'I will forge for you two matching keys, part of an "asymmetric" pair.', + # 'Please, watch me work.', + + # None,{tw.indent(ART_KEY,' '*5)+'\n'}, + + # '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) + # self.status( + # None,{ART_KEY_PAIR}, + # 'A matching set of keys have been generated.', + # None,{ART_KEY_PAIR2A+'\n\nA matching set of keys have been generated.'}, + # '1) First, I have made a "public key" which you can share with anyone:', + # f'{repr(pubkey)}', + # 'This key is a randomly-generated binary string, which acts as your "address" on Komrade.', + # 'By sharing this key with someone, you enable them to 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 phone to phone:', + # {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}.)' + # ) - # make and save keys locally - self.log(f'{KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_CLIENT + KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_SERVER}!!!!!') - uri_id,keys_returned = self.persona.forge_new_keys( - name=name, - passphrase=None, - keys_to_save = [], - keys_to_return = KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_CLIENT + KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_SERVER + # private keys + # self.status(None, + # {ART_KEY_PAIR2B}, + # 'Second, I have forged a matching "private key":', + # f'{repr(privkey)}', + # 'With it, you can decrypt any message sent to you via your public key.', + # 'You you should never, ever give this key to anyone.', + # 'In fact, this key is so dangerous that I will immediately destroy it by splitting it into two half-keys:' + # ) + # 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.", + ) - self.status('got back',dict_format(keys_returned)) - #self.log(f'my new uri is {uri_id} and I got new keys!: {dict_format(keys_returned)}') - self.status( - 'Generating public key now: ',5,'\n\t',repr(KomradeAsymmetricPublicKey(keychain['pubkey'])),'\n\n', - 'Generating private key now: ',5,'\n\t',repr(KomradeAsymmetricPrivateKey(keychain['privkey'])), - clear=False,pause=False,end=' ',speed=2 + None,{ART_KEY_PAIR4Z1}, + 'So choosing a password is an important thing!' ) + + if not passphrase: + 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.", + ) + res = self.status(None, + {ART_FROG_BLENDER,True}, + "@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') + hashed_str = hasher(str_to_hash.encode()) + res = self.status( + '@Hasher: '+hashed_str + ) + + res = self.status( + '@Keymaker: See? Ok, now type in a password.' + ('str_to_hash',f'@{name}: ',getpass) + ) + str_to_hash = res.get('vals').get('str_to_hash') + hashed_pass1 = hasher(str_to_hash.encode()) + res = self.status( + '@Hasher: '+hashed_pass1 + ) + + res = self.status( + '@Keymaker: Whatever you entered, it\'s already forgotten. That hashed mess is all that remains.', + 'Now type in the same password one more time to verify it:', + ('str_to_hash',f'@{name}: ',getpass) + ) + str_to_hash = res.get('vals').get('str_to_hash') + hashed_pass2 = hasher(str_to_hash.encode()) + res = self.status( + '@Hasher: '+hashed_pass2 + ) + + if hashed_pass1==hashed_pass2: + self.status('The passwords matched.') + else: + self.status('The passwords did not match.') + + + + + + + + + + + + + + - # save the ones we should on server - data = { - **{'name':name, 'passphrase':self.crypt_keys.hash(passphrase.encode()), ROUTE_KEYNAME:'register_new_user'}, - **keys_returned - } - self.log('sending to server:',dict_format(data,tab=2)) - # msg_to_op = self.compose_msg_to(data, self.op) - - # ring operator - # call from phone since I don't have pubkey on record on Op yet - resp_msg_obj = self.phone.ring_ring(data) - self.log('register got back from op:',dict_format(resp_msg_obj,tab=2)) def run_cli(): cli = CLI() - asyncio.run(cli.run('/register',name='elon')) + cli.run('/register','elon') #'/register',name='elon') if __name__=='__main__': run_cli() diff --git a/komrade/constants.py b/komrade/constants.py index cd18f84..2a07949 100644 --- a/komrade/constants.py +++ b/komrade/constants.py @@ -105,8 +105,8 @@ KEYMAKER_DEFAULT_KEY_TYPES = { 'adminkey':KEY_TYPE_SYMMETRIC_WITHOUT_PASSPHRASE, 'pubkey_decr':KEY_TYPE_SYMMETRIC_WITHOUT_PASSPHRASE, - 'privkey_decr':KEY_TYPE_SYMMETRIC_WITHOUT_PASSPHRASE, - 'adminkey_decr':KEY_TYPE_SYMMETRIC_WITHOUT_PASSPHRASE, + 'privkey_decr':KEY_TYPE_SYMMETRIC_WITH_PASSPHRASE, + 'adminkey_decr':KEY_TYPE_SYMMETRIC_WITH_PASSPHRASE, 'pubkey_decr_decr':KEY_TYPE_SYMMETRIC_WITHOUT_PASSPHRASE, 'privkey_decr_decr':KEY_TYPE_SYMMETRIC_WITH_PASSPHRASE, @@ -151,7 +151,6 @@ PATH_OPERATOR_WEB_CONTACTS_DIR = '/home/ryan/www/website-komrade/.contacts' PATH_OPERATOR_WEB_CONTACT_OP_URL = f'http://{KOMRADE_URL}/.contacts/TheOperator.png' PATH_OPERATOR_WEB_CONTACT_PH_URL = f'http://{KOMRADE_URL}/.contacts/TheTelephone.png' -PAUSE_LOGGER = True # dangerous! leave on only if absolutely necessary for initial dev ALLOW_CLEARNET = True @@ -182,9 +181,11 @@ DEFAULT_USER_SETTINGS = { 'visibility':VISIBILITY_TYPE_SEMIPUBLIC } -SHOW_LOG = True -SHOW_STATUS = True +SHOW_LOG = 1 +SHOW_STATUS = 1 +PAUSE_LOGGER = 1 CLI_TITLE = 'KOMRADE' -CLI_FONT = 'colossal' \ No newline at end of file +CLI_FONT = 'clr5x6'#'colossal' +STATUS_LINE_WIDTH = 50 \ No newline at end of file diff --git a/komrade/utils.py b/komrade/utils.py index a4cf81c..07dec53 100644 --- a/komrade/utils.py +++ b/komrade/utils.py @@ -26,10 +26,14 @@ def log(*x): def clear_screen(): import os + # pass os.system('cls' if os.name == 'nt' else 'clear') def do_pause(): - input('') + try: + input('') + except KeyboardInterrupt: + exit('\n\nGoodbye.') def dict_format(d, tab=0): @@ -47,11 +51,11 @@ def dict_format(d, tab=0): # s.append('%s%r: %s (%s),\n' % (' '*tab, k, v, type(v).__name__)) s.append('%s%r: %s,\n\n' % (' '*tab, k, reppr(v))) - s.append('%s}' % (' '*tab)) + s.append('%s}' % (' '*(tab-2))) return ''.join(s) import inspect,time -from komrade.constants import PAUSE_LOGGER,SHOW_LOG,SHOW_STATUS +from komrade.constants import * class Logger(object): def log(self,*x,pause=PAUSE_LOGGER,clear=PAUSE_LOGGER): if not SHOW_LOG: return @@ -65,9 +69,22 @@ class Logger(object): if pause: do_pause() if pause: clear_screen() # except KeyboardInterrupt: - exit() + # exit() - def status(self,*msg,pause=True,clear=False,ticks=[],tab=2,speed=2,end=None): + def print(*x,width=STATUS_LINE_WIDTH,end='\n',indent=1,scan=False,**y): + if not scan and not width: + print(*x,end=end,**y) + else: + import textwrap as tw + xs=end.join(str(xx) for xx in x if type(xx)==str) + if width: + xw = [_.strip() for _ in tw.wrap(xs,width=width)] + # xw = [_ for _ in tw.wrap(xs,width=width)] + xs=end.join(xw) + xs = tw.indent(xs,' '*indent) + print(xs) if scan==False else scan_print(xs) + + def status(self,*msg,pause=True,clear=False,ticks=[],tab=2,speed=10,end=None,indent=0,width=80,scan=False): import random if not SHOW_STATUS: return # if len(msg)==1 and type(msg[0])==str: @@ -76,17 +93,33 @@ class Logger(object): paras=[] res={} for para in msg: + indentstr=' '*indent plen = para if type(para)==int or type(para)==float else None if type(para) in {int,float}: plen=int(para) - for i in range(plen * speed): + # print() + print(' '*indent,end='',flush=True) + for i in range(plen): tick = ticks[i] if i