2020-09-08 11:23:41 +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 *
2020-09-11 14:35:47 +00:00
from komrade . backend . keymaker import *
2020-09-17 04:16:48 +00:00
from komrade . backend . messages import Message
2020-09-08 11:23:41 +00:00
2020-09-29 08:58:24 +00:00
import logging , asyncio
2020-09-20 19:34:45 +00:00
logger = logging . getLogger ( __name__ )
2020-09-14 06:02:17 +00:00
class KomradeX ( Caller ) :
2020-09-08 11:23:41 +00:00
2020-09-20 19:34:45 +00:00
def __init__ ( self , name = None , pubkey = None , callbacks = { } , getpass_func = None ) :
# logger.info('booting KomradeX with getpass_func:',getpass_func)
super ( ) . __init__ ( name = name , callbacks = callbacks , getpass_func = getpass_func )
2020-09-19 18:54:57 +00:00
self . log ( f ' Starting up with callbacks: { self . _callbacks } ' )
2020-09-24 15:21:16 +00:00
# self.boot(create=False)
2020-09-18 05:58:28 +00:00
# special?
if self . name == WORLD_NAME :
if os . path . exists ( PATH_SUPER_SECRET_OP_KEY ) :
print ( f ' Dare I claim to be the one true @ { WORLD_NAME } ? ' )
with open ( PATH_SUPER_SECRET_OP_KEY , ' rb ' ) as f :
#pass_encr=f.read()
opk1 , opk2 , privkey_decr , privkey_encr = b64dec ( f . read ( ) ) . split ( BSEP )
privkey_decr_obj = KomradeSymmetricKeyWithoutPassphrase ( privkey_decr )
privkey_encr_obj = KomradeEncryptedAsymmetricPrivateKey ( privkey_encr )
self . _keychain [ ' privkey_decr ' ] = privkey_decr_obj
self . _keychain [ ' privkey_encr ' ] = privkey_encr_obj
2020-09-08 12:11:13 +00:00
2020-09-13 18:55:14 +00:00
def boot ( self , create = False , ping = False ) :
2020-09-24 15:21:16 +00:00
return
2020-09-08 12:11:13 +00:00
2020-09-24 15:21:16 +00:00
# # Do I already have my keys?
# # yes? -- login
2020-09-14 07:58:26 +00:00
2020-09-24 15:21:16 +00:00
# keys = self.keychain()
# # self.log(f'booting {self}!',dict_format(keys))
# if keys.get('pubkey') and keys.get('privkey'):
# # self.log('already booted! @'+self.name)
# return True
2020-09-09 14:38:37 +00:00
2020-09-24 15:21:16 +00:00
# if self.exists_locally_as_account():
# self.log(f'this account (@{self.name}) can be logged into')
# return #self.login()
2020-09-13 18:55:14 +00:00
2020-09-24 15:21:16 +00:00
# elif self.exists_locally_as_contact():
# self.log(f'this account (@{self.name}) is a contact')
# return #pass #???
2020-09-13 18:55:14 +00:00
2020-09-24 15:21:16 +00:00
# elif ping and self.exists_on_server():
# self.log(f'this account exists on server. introduce?')
# return
2020-09-13 18:55:14 +00:00
2020-09-24 15:21:16 +00:00
# elif create:
# self.log('account is free: register?')
# return self.register()
2020-09-13 18:55:14 +00:00
2020-09-09 14:38:37 +00:00
2020-09-14 04:43:59 +00:00
def exists_locally ( self ) :
2020-09-24 15:21:16 +00:00
pubkey = self . find_pubkey ( )
return bool ( pubkey )
2020-09-09 17:34:19 +00:00
def exists_locally_as_contact ( self ) :
2020-09-24 15:21:16 +00:00
pubkey = self . find_pubkey ( )
if not pubkey : return False
uri = pubkey . data_b64
if self . crypt_keys . get ( uri , prefix = ' /privkey_encr/ ' ) :
return False
2020-09-27 11:32:23 +00:00
self . log ( pubkey , self . name , ' ??? ' )
2020-09-24 15:21:16 +00:00
return True
2020-09-09 17:34:19 +00:00
2020-09-13 18:55:14 +00:00
def exists_locally_as_account ( self ) :
2020-09-24 15:21:16 +00:00
#return bool(self.pubkey) and bool(self.privkey_encr)
pubkey = self . find_pubkey ( )
2020-09-24 15:46:34 +00:00
self . log ( ' found pubkey: ' , pubkey )
2020-09-24 15:21:16 +00:00
if not pubkey : return False
uri = pubkey . data_b64
2020-09-24 15:46:34 +00:00
self . log ( ' crypt???? ' , self . crypt_keys . fn )
res = self . crypt_keys . get ( uri , prefix = ' /privkey_encr/ ' )
if res :
self . log ( ' found privkey_encr: ' , res )
2020-09-24 15:21:16 +00:00
return True
return False
2020-09-09 17:34:19 +00:00
def exists_on_server ( self ) :
2020-09-10 21:32:59 +00:00
answer = self . phone . ring_ring ( {
2020-09-09 18:31:36 +00:00
' _route ' : ' does_username_exist ' ,
' name ' : self . name
} )
2020-09-09 22:38:57 +00:00
self . log ( ' answer?? ' , answer )
2020-09-10 08:08:50 +00:00
return answer
2020-09-08 11:23:41 +00:00
2020-09-17 10:37:58 +00:00
## Talking with Operator
2020-09-22 19:34:03 +00:00
async def ring_ring ( self , msg , route = None , * * y ) :
logger . info ( ' got here 2! ' )
2020-09-17 10:37:58 +00:00
if type ( msg ) == dict and not ROUTE_KEYNAME in msg :
msg [ ROUTE_KEYNAME ] = route
2020-09-22 19:34:03 +00:00
return await super ( ) . ring_ring ( msg , caller = self , * * y )
2020-09-17 10:37:58 +00:00
2020-09-29 08:58:24 +00:00
def refresh ( self , include_posts = True ) :
res = asyncio . run ( self . get_updates ( include_posts = include_posts ) )
return { ' res ' : res , ' success ' : res . get ( ' success ' ) , ' status ' : res . get ( ' status ' , ' ' ) }
2020-09-17 10:37:58 +00:00
####################################
## (1) Logging in and registering ##
####################################
2020-09-22 19:34:03 +00:00
async def log_async ( self , * x , * * y ) :
return self . log ( * x , * * y )
2020-09-17 10:37:58 +00:00
2020-09-16 14:47:51 +00:00
def register ( self , name = None , passphrase = None , is_group = None , show_intro = 0 , show_body = True , logfunc = None ) :
2020-09-14 06:02:17 +00:00
# global PHONEBOOK
2020-09-13 14:15:06 +00:00
# print('got name:',name)
2020-09-12 13:11:39 +00:00
## Defaults
2020-09-10 17:52:07 +00:00
if name and not self . name : self . name = name
if not name and self . name : name = self . name
2020-09-13 13:57:06 +00:00
# if not name and not self.name: name=''
2020-09-13 14:15:06 +00:00
# print('got name',name)
2020-09-16 14:47:51 +00:00
if not logfunc : logfunc = self . log
2020-09-14 04:43:59 +00:00
# already have it?
if self . exists_locally_as_account ( ) :
2020-09-14 06:20:02 +00:00
return { ' success ' : False , ' status ' : ' You have already created this account. ' }
2020-09-14 04:43:59 +00:00
if self . exists_locally_as_contact ( ) :
2020-09-14 06:20:02 +00:00
return { ' success ' : False , ' status ' : ' This is already a contact of yours ' }
2020-09-14 04:43:59 +00:00
2020-09-12 13:11:39 +00:00
## 1) Have name?
2020-09-16 15:10:19 +00:00
clear_screen ( )
2020-09-13 13:57:06 +00:00
tolog = ' '
2020-09-11 14:35:47 +00:00
if SHOW_STATUS and show_intro :
2020-09-13 13:57:06 +00:00
self . name = name = self . cli . status_keymaker_part1 ( name )
2020-09-12 13:17:54 +00:00
elif not name :
2020-09-13 13:57:06 +00:00
self . name = name = input ( ' \n Hello, this is Komrade @ ' )
2020-09-16 15:08:57 +00:00
logfunc ( ' I would like to sign up for the socialist network revolution. ' , flush = True , komrade_name = name , pause = True )
2020-09-16 12:44:18 +00:00
# do_pause()
2020-09-13 13:57:06 +00:00
else :
2020-09-16 15:19:38 +00:00
logfunc ( f ' Hello, this is Komrade @ { name } . \n \n I would like to sign up for the socialist network revolution. ' , pause = True , komrade_name = name )
2020-09-16 12:44:18 +00:00
# do_pause()
2020-09-13 13:57:06 +00:00
2020-09-16 12:44:18 +00:00
# clear_screen()
2020-09-16 15:19:38 +00:00
logfunc ( f ' Excellent. But to communicate with komrades securely, you must first cut your public & private encryption keys. ' , pause = True , clear = True )
2020-09-13 13:57:06 +00:00
# do_pause()
2020-09-12 13:11:39 +00:00
## 2) Make pub public/private keys
2020-09-11 14:35:47 +00:00
keypair = KomradeAsymmetricKey ( )
pubkey , privkey = keypair . pubkey_obj , keypair . privkey_obj
2020-09-16 15:56:47 +00:00
logfunc ( f ' I have cut for you a private and public asymmetric key pair, using the iron-clad Elliptic curve algorithm: \n \n (1) { pubkey } \n \n (2) { privkey } { ART_KEY_PAIR } ' , clear = True , pause = True )
2020-09-16 16:15:45 +00:00
self . _keychain [ ' pubkey ' ] = pubkey
self . _keychain [ ' privkey ' ] = privkey
self . log ( ' My keychain now looks like: ' , dict_format ( self . keychain ( ) ) )
2020-09-11 14:35:47 +00:00
2020-09-16 15:54:45 +00:00
### PUBLIC KEY
qr_str = self . qr_str ( pubkey . data_b64 )
2020-09-16 16:01:04 +00:00
logfunc ( f ' (1) You may store your public key both on your device hardware, as well as share it with anyone you wish: \n \n { pubkey . data_b64_s } \n \n It will also be stored as a QR code on your device: \n { qr_str } ' , pause = True , clear = True )
2020-09-16 16:15:45 +00:00
logfunc ( ' You must also register your username and public key with Komrade @Operator on the remote server. \n \n Shall Komrade @Telephone send them over? ' , pause = False , clear = False ) #),dict_format(data,tab=2),pause=True)
2020-09-20 19:34:45 +00:00
ok_to_send = ' y ' #input(f'\n@{name}: [Y/n] ')
2020-09-16 15:54:45 +00:00
if ok_to_send . strip ( ) . lower ( ) == ' n ' :
logfunc ( ' Cancelling registration. ' )
return
2020-09-16 16:51:31 +00:00
else :
print ( )
logfunc ( ' Connecting you to the @Operator... ' , komrade_name = ' Telephone ' )
2020-09-16 17:31:09 +00:00
# print()
2020-09-17 15:13:12 +00:00
# time.sleep(1)
2020-09-16 15:54:45 +00:00
## CALL OP WITH PUBKEY
2020-09-29 08:58:24 +00:00
resp_msg_d = asyncio . run ( self . ring_ring (
2020-09-16 15:54:45 +00:00
{
' name ' : name ,
' pubkey ' : pubkey . data ,
} ,
route = ' register_new_user '
2020-09-29 08:58:24 +00:00
) )
2020-09-16 17:31:09 +00:00
# print()
clear_screen ( )
logfunc ( resp_msg_d . get ( ' status ' ) + ART_OLDPHONE4 , komrade_name = ' Operator ' , pause = True )
2020-09-16 16:15:45 +00:00
print ( )
2020-09-16 15:54:45 +00:00
if not resp_msg_d . get ( ' success ' ) :
2020-09-16 17:31:09 +00:00
logfunc ( ''' That ' s too bad. Cancelling registration for now. ''' , pause = True , clear = True )
2020-09-16 15:54:45 +00:00
return
2020-09-16 16:26:43 +00:00
# clear_screen()
2020-09-16 17:31:09 +00:00
logfunc ( ' Great. Komrade @Operator now has your name and public key on file (and nothing else!). ' , pause = True , clear = True )
2020-09-16 15:54:45 +00:00
2020-09-16 16:51:31 +00:00
logfunc ( f " (2) Your PRIVATE key, on the other hand, must be stored only on your device hardware. " , pause = True )
logfunc ( ''' Your private key is so sensitive we ' ll even encrypt it before storing it. ''' , pause = True , use_prefix = False )
2020-09-16 15:54:45 +00:00
2020-09-16 16:26:43 +00:00
2020-09-16 15:54:45 +00:00
2020-09-12 13:11:39 +00:00
## 3) Have passphrase?
2020-09-11 17:17:21 +00:00
if SHOW_STATUS and not passphrase :
2020-09-12 14:32:03 +00:00
passphrase = self . cli . status_keymaker_part2 ( name , passphrase , pubkey , privkey , hasher , self )
2020-09-20 19:34:45 +00:00
elif not passphrase and self . getpass_func :
passphrase = self . getpass_func ( WHY_MSG )
2020-09-11 14:35:47 +00:00
else :
2020-09-13 05:33:37 +00:00
if not passphrase : passphrase = DEBUG_DEFAULT_PASSPHRASE
2020-09-13 05:39:38 +00:00
while not passphrase :
2020-09-16 16:26:43 +00:00
# logfunc('Please type a password:',use_prefix=False)
logfunc ( ''' Please enter a memorable password to generate a (symmetric AES) encryption key with: ''' , use_prefix = True )
2020-09-16 16:51:31 +00:00
passphrase = getpass ( f ' \n @ { self . name } : ' )
# clear_screen()
2020-09-12 13:11:39 +00:00
## 4) Get hashed password
2020-09-16 15:54:45 +00:00
2020-09-12 14:32:03 +00:00
passhash = hasher ( passphrase )
2020-09-16 15:54:45 +00:00
privkey_decr = KomradeSymmetricKeyWithPassphrase ( passhash = passhash )
2020-09-16 16:51:31 +00:00
print ( )
2020-09-16 17:31:09 +00:00
logfunc ( f ''' Let ' s immediately run whatever you typed through a 1-way hashing algorithm (SHA-256), inflating it to (redacted): \n \n { make_key_discreet_str ( passhash ) } ''' , pause = True , clear = False )
2020-09-16 15:54:45 +00:00
2020-09-11 17:17:21 +00:00
privkey_encr = privkey_decr . encrypt ( privkey . data )
2020-09-12 07:55:23 +00:00
privkey_encr_obj = KomradeEncryptedAsymmetricPrivateKey ( privkey_encr )
2020-09-16 16:15:45 +00:00
self . _keychain [ ' privkey_encr ' ] = privkey_encr_obj
self . log ( ' My keychain now looks like v2: ' , dict_format ( self . keychain ( ) ) )
2020-09-16 16:51:31 +00:00
logfunc ( ' With this inflated password we can encrypt your super-sensitive private key. ' , pause = True , clear = True )
2020-09-16 17:31:09 +00:00
logfunc ( f " Your original private key looks like this (redacted): \n \n { privkey } " , pause = True , clear = False )
2020-09-12 13:17:54 +00:00
2020-09-16 17:31:09 +00:00
logfunc ( f " After we encrypt it with your passworded key, it looks like this (redacted): \n \n { privkey_encr_obj } " , pause = True , clear = False )
2020-09-16 15:54:45 +00:00
logfunc ( ' Only this encrypted version is stored. ' , pause = True , clear = True )
2020-09-16 16:15:45 +00:00
2020-09-12 13:17:54 +00:00
2020-09-14 04:43:59 +00:00
# More narration?
if SHOW_STATUS : self . cli . status_keymaker_part3 ( privkey , privkey_decr , privkey_encr , passphrase )
2020-09-10 21:32:59 +00:00
2020-09-13 10:57:06 +00:00
2020-09-13 09:42:22 +00:00
self . name = resp_msg_d . get ( ' name ' )
2020-09-13 13:04:44 +00:00
pubkey_b = resp_msg_d . get ( ' pubkey ' )
2020-09-14 04:43:59 +00:00
assert pubkey_b == pubkey . data
2020-09-14 06:35:40 +00:00
uri_id = pubkey . data_b64
2020-09-13 13:04:44 +00:00
sec_login = resp_msg_d . get ( ' secret_login ' )
2020-09-16 17:31:09 +00:00
# stop
2020-09-13 09:42:22 +00:00
2020-09-16 17:31:09 +00:00
logfunc ( f ''' Saving keys to device: ''' , pause = True )
logfunc ( f ''' (1) { pubkey } ''' , pause = True , use_prefix = False )
logfunc ( f ''' (2) { privkey_encr_obj } ''' , pause = True , use_prefix = False )
logfunc ( f ''' (3) [Shared Login Secret with @Operator] \n ( { make_key_discreet ( sec_login ) } ) ''' , pause = True , use_prefix = False )
# print()
2020-09-14 06:35:40 +00:00
self . crypt_keys . set ( name , pubkey_b , prefix = ' /pubkey/ ' )
self . crypt_keys . set ( uri_id , name , prefix = ' /name/ ' )
2020-09-14 04:43:59 +00:00
self . crypt_keys . set ( uri_id , sec_login , prefix = ' /secret_login/ ' )
2020-09-10 09:34:34 +00:00
2020-09-14 08:25:55 +00:00
# store privkey pieces
self . crypt_keys . set ( uri_id , privkey_encr_obj . data , prefix = ' /privkey_encr/ ' )
# just to show we used a passphrase -->
self . crypt_keys . set ( uri_id , KomradeSymmetricKeyWithPassphrase . __name__ , prefix = ' /privkey_decr/ ' )
2020-09-13 09:42:22 +00:00
# save qr too:
2020-09-16 17:31:09 +00:00
_fnfn = self . save_uri_as_qrcode ( uri_id )
logfunc ( f ' Also saving public key as QR code to: { _fnfn } . ' , pause = True , clear = False , use_prefix = False )
2020-09-13 09:42:22 +00:00
# done!
2020-09-16 17:31:09 +00:00
print ( )
logfunc ( f ' Congratulations. Welcome, { self } . ' , pause = True , clear = True )
2020-09-16 15:25:11 +00:00
# self.help()
2020-09-08 15:14:48 +00:00
2020-09-19 15:04:12 +00:00
##
# last minute: get posts
if ' res_posts ' in resp_msg_d and resp_msg_d [ ' res_posts ' ] . get ( ' success ' ) :
id2post = resp_msg_d . get ( ' res_posts ' ) . get ( ' posts ' , { } )
if id2post :
self . log ( ' found starter posts: ' , list ( id2post . keys ( ) ) )
self . save_posts ( id2post )
resp_msg_d [ ' status ' ] + = f ' You \' ve got { len ( id2post ) } new posts and 0 new messages. '
return resp_msg_d
###
2020-09-16 17:31:09 +00:00
return resp_msg_d
2020-09-21 08:58:57 +00:00
def get_secret_login ( self , pubkey ) :
2020-09-13 11:18:04 +00:00
return self . crypt_keys . get (
2020-09-21 08:58:57 +00:00
b64enc ( pubkey ) ,
2020-09-13 12:31:33 +00:00
prefix = ' /secret_login/ '
2020-09-13 11:18:04 +00:00
)
2020-09-21 08:58:57 +00:00
@property
def secret_login ( self ) :
return self . get_secret_login ( self . pubkey . data_b64 )
2020-09-13 12:27:45 +00:00
def login ( self , passphrase = None ) :
2020-09-14 07:58:26 +00:00
# what keys do I have?
keys = self . keychain ( )
# check hardware
2020-09-21 08:58:57 +00:00
if not ' pubkey ' in keys or not self . pubkey :
2020-09-14 07:58:26 +00:00
emsg = ''' Login impossible. I do not have this komrade ' s public key, much less private one. '''
# self.log()
return { ' success ' : False , ' status ' : emsg }
if not ' privkey_encr ' in keys :
emsg = ''' Login impossible. I do not have this komrade ' s private key on this hardware. '''
self . log ( emsg )
return { ' success ' : False , ' status ' : emsg }
2020-09-13 11:18:04 +00:00
2020-09-14 07:58:26 +00:00
if not ' privkey ' in keys :
emsg = ''' Login failed. Private key did not unlock from passphrase. '''
return { ' success ' : False , ' status ' : emsg }
2020-09-13 11:18:04 +00:00
# compose message
msg = {
' name ' : self . name ,
2020-09-14 07:58:26 +00:00
' pubkey ' : keys [ ' pubkey ' ] . data ,
2020-09-21 08:58:57 +00:00
' secret_login ' : self . get_secret_login ( keys [ ' pubkey ' ] . data )
2020-09-13 11:18:04 +00:00
}
# ask operator and get response
2020-09-16 12:41:42 +00:00
resp_msg_d = self . ring_ring (
2020-09-13 11:18:04 +00:00
msg ,
route = ' login '
)
2020-09-16 13:46:54 +00:00
# print('got resp_msg_d:',resp_msg_d)
2020-09-16 10:53:55 +00:00
2020-09-13 11:18:04 +00:00
# get result
2020-09-16 12:41:42 +00:00
self . log ( ' Got resp_msg_d back from operator: ' , resp_msg_d )
2020-09-13 11:18:04 +00:00
2020-09-16 12:41:42 +00:00
# check msgs?
2020-09-17 09:26:47 +00:00
if ' inbox ' in resp_msg_d :
self . do_check_msgs ( resp_msg_d [ ' inbox ' ] )
2020-09-16 12:41:42 +00:00
resp_msg_d [ ' res_refresh ' ] = self . refresh ( check_msgs = False ) # already done in previous line
self . log ( ' --> ' , resp_msg_d )
return resp_msg_d
2020-09-13 11:18:04 +00:00
2020-09-13 18:06:41 +00:00
2020-09-17 10:37:58 +00:00
########################
## (2) MEETING PEOPLE ##
########################
2020-09-14 08:48:23 +00:00
def contacts ( self ) :
# who do I know?
2020-09-26 12:11:56 +00:00
qr_names = sorted ( [
fn . split ( ' .png ' ) [ 0 ] for fn in os . listdir ( PATH_QRCODES )
] )
ppl = [ Komrade ( name ) for name in qr_names ]
ppl = [ p for p in ppl if p . exists_locally_as_contact ( ) ]
return ppl
2020-09-13 18:06:41 +00:00
2020-09-17 10:37:58 +00:00
### MEETING PEOLPE
def meet ( self , name = None , pubkey = None , returning = False ) :
if not name and not pubkey :
return { ' success ' : False , ' status ' : ' Meet whom? ' }
# already met this person?
2020-09-17 20:06:30 +00:00
keystr = self . name + ' -> ' + name
2020-09-17 20:05:43 +00:00
# if self.crypt_keys.get(
# keystr,
# prefix='/met/'
# ):
# return {
# 'success':False,
# 'status':f'You have already sent an introduction to @{name}. It would be rude to send another.'
# }
2020-09-13 11:18:04 +00:00
2020-09-17 10:37:58 +00:00
# send msg to op
msg_to_op = {
' name ' : self . name ,
' secret_login ' : self . secret_login ,
' pubkey ' : self . uri ,
2020-09-08 15:14:48 +00:00
2020-09-17 10:37:58 +00:00
' meet_name ' : name ,
' meet_pubkey ' : pubkey ,
' returning ' : returning
}
self . log ( ' msg_to_op ' , msg_to_op )
2020-09-17 06:19:41 +00:00
2020-09-17 10:37:58 +00:00
res = self . ring_ring (
msg_to_op ,
2020-09-17 12:23:11 +00:00
route = ' introduce '
2020-09-17 06:19:41 +00:00
)
2020-09-17 10:37:58 +00:00
self . log ( ' res from op <- ' , res )
2020-09-17 06:19:41 +00:00
2020-09-17 10:37:58 +00:00
# record that I've already tried this
2020-09-18 05:32:25 +00:00
# self.crypt_keys.set(
# keystr,
# b'y',
# prefix='/met/'
# )
2020-09-17 06:19:41 +00:00
2020-09-17 10:37:58 +00:00
# return result
2020-09-18 05:32:25 +00:00
succ = res . get ( ' success ' )
if succ :
msgd = res . get ( ' msg_d ' , { } )
2020-09-18 13:16:55 +00:00
msg = msgd . get ( ' msg ' , { } ) . get ( ' txt ' , ' [?] ' )
2020-09-18 05:32:25 +00:00
toname = msgd . get ( ' to_name ' )
status = f """ I sent the following message to { toname } : \n \n " { msg } " """
else :
status = res . get ( ' status ' )
return {
' status ' : status ,
' success ' : succ ,
' res ' : res
}
2020-09-17 10:37:58 +00:00
2020-09-17 06:41:02 +00:00
2020-09-17 06:19:41 +00:00
2020-09-17 04:42:18 +00:00
2020-09-17 06:19:41 +00:00
2020-09-17 10:37:58 +00:00
###################
## (3) MESSAGING ##
###################
2020-09-17 04:16:48 +00:00
2020-09-14 07:58:26 +00:00
def msg ( self , someone , something ) :
2020-09-29 09:13:19 +00:00
return asyncio . run ( self . msg_async ( someone , something ) )
2020-09-29 09:14:05 +00:00
async def msg_async ( self , someone , something ) :
2020-09-14 07:58:26 +00:00
# find or boot
someone = Komrade ( someone )
self . log ( f ' found { someone } ' )
# can we?
if not someone . pubkey :
self . log ( f ''' Don ' t know the public key of { someone } ! ''' )
2020-09-17 12:23:11 +00:00
# write them message
2020-09-13 19:42:04 +00:00
msg_obj = self . compose_msg_to (
2020-09-14 07:58:26 +00:00
something ,
someone
2020-09-13 19:42:04 +00:00
)
self . log ( ' composed msg: ' , msg_obj )
2020-09-14 07:58:26 +00:00
2020-09-17 12:23:11 +00:00
# encrypt it
2020-09-13 19:42:04 +00:00
msg_obj . encrypt ( )
2020-09-14 07:58:26 +00:00
# attaching
direct_msg_data = msg_obj . msg
self . log ( ' going to send only this to op: ' , direct_msg_data )
# enclosing
msg_to_op = {
2020-09-17 08:16:52 +00:00
' deliver_msg ' : {
' from ' : self . pubkey . data_b64 ,
' from_name ' : self . name ,
' to ' : someone . pubkey . data_b64 ,
' to_name ' : someone . name ,
' msg ' : direct_msg_data ,
2020-09-28 19:17:38 +00:00
' timestamp ' : time . time ( )
2020-09-17 08:16:52 +00:00
}
2020-09-14 07:58:26 +00:00
}
self . log ( ' going to send msg to op? ' , msg_to_op )
2020-09-09 15:30:34 +00:00
2020-09-17 12:23:11 +00:00
# dial operator
2020-09-29 09:13:19 +00:00
res = await self . ring_ring (
2020-09-14 07:58:26 +00:00
msg_to_op ,
route = ' deliver_msg '
)
2020-09-17 12:23:11 +00:00
self . log ( ' --> ' , res )
return res
2020-09-09 15:30:34 +00:00
2020-09-18 10:33:13 +00:00
#def post(self,something):
# return self.msg(WORLD_NAME,something)
2020-09-17 12:23:11 +00:00
2020-09-29 08:58:24 +00:00
def post ( self , something ) :
return asyncio . run ( self . post_async ( something ) )
async def post_async ( self , something ) :
2020-09-18 10:45:30 +00:00
# sign it!
self . log ( ' something = ' , something )
something_b = pickle . dumps ( something )
self . log ( ' something_b = ' , something_b )
something_b_signed = ssign (
self . privkey . data ,
something_b
)
self . log ( ' something_b_signed = ' , something_b_signed )
2020-09-18 11:15:16 +00:00
# something_b_signed_b64 = b64enc(
# something_b_signed
# )
# self.log('something_b_signed_b64 =',something_b_signed_b64)
# encrypt package to world (or op?)
2020-09-18 11:39:32 +00:00
world = Komrade ( WORLD_NAME )
something_b_signed_encr4world = SMessage (
self . privkey . data ,
world . pubkey . data
) . wrap ( something_b_signed )
self . log ( something_b_signed_encr4world )
2020-09-18 11:15:16 +00:00
post_pkg_d = {
2020-09-18 11:39:32 +00:00
' post ' : something_b_signed_encr4world ,
2020-09-18 11:15:16 +00:00
' post_from ' : self . uri ,
2020-09-28 19:06:39 +00:00
' post_from_name ' : self . name ,
' post_timestamp ' : time . time ( ) ,
2020-09-18 11:15:16 +00:00
}
self . log ( ' post_pkg_d = ' , post_pkg_d )
post_pkg_b = pickle . dumps ( post_pkg_d )
self . log ( ' post_pkg_b = ' , post_pkg_d )
2020-09-18 11:39:32 +00:00
# post_pkg_b_encr4world = SMessage(
# self.privkey.data,
# Komrade(WORLD_NAME).pubkey.data
# ).wrap(post_pkg_b)
# self.log(post_pkg_b_encr4world)
2020-09-18 10:45:30 +00:00
2020-09-26 10:12:16 +00:00
res_op = await self . ring_ring (
2020-09-18 10:33:13 +00:00
{
2020-09-18 11:39:32 +00:00
' data ' : post_pkg_b
2020-09-18 10:33:13 +00:00
} ,
2020-09-29 09:19:02 +00:00
route = ' deliver_post '
2020-09-18 10:33:13 +00:00
)
self . log ( ' post res from Op <- ' , res_op )
return res_op
2020-09-17 12:23:11 +00:00
2020-09-17 12:57:12 +00:00
@property
def login_details ( self ) :
return {
' name ' : self . name ,
' secret_login ' : self . secret_login ,
' pubkey ' : self . uri
}
def save_posts ( self ,
id2post ,
inbox_prefix = ' /feed/ ' ,
post_prefix = ' /post/ ' ) :
# update inbox
2020-09-17 16:06:12 +00:00
new_inbox = list ( id2post . keys ( ) )
2020-09-17 18:48:00 +00:00
self . log ( ' new_inbox -> ' , new_inbox , inbox_prefix , post_prefix )
2020-09-17 12:57:12 +00:00
inbox = self . get_inbox_crypt (
prefix = inbox_prefix
)
inbox . prepend ( new_inbox )
2020-09-17 18:18:20 +00:00
self . log ( ' new vals = ' , inbox . values )
2020-09-17 12:57:12 +00:00
# update posts
for post_id , post in id2post . items ( ) :
2020-09-17 18:51:53 +00:00
self . log ( f ' saving post! --> { post_prefix } { post_id } --> { post } ' )
2020-09-17 16:33:31 +00:00
2020-09-17 12:57:12 +00:00
self . crypt_data . set (
post_id ,
post ,
2020-09-17 16:33:31 +00:00
prefix = post_prefix ,
override = True
2020-09-17 12:57:12 +00:00
)
res = {
' success ' : True ,
' status ' : f ' Saved { len ( id2post ) } posts. '
}
self . log ( ' --> ' , res )
return res
2020-09-17 08:16:52 +00:00
2020-09-17 12:57:12 +00:00
def save_msgs ( self , id2msg ) :
return self . save_posts (
id2post = id2msg ,
inbox_prefix = ' /inbox/ ' ,
2020-09-17 16:23:49 +00:00
post_prefix = ' /post/ '
2020-09-17 12:57:12 +00:00
)
2020-09-17 12:23:11 +00:00
## Getting updates
2020-09-22 19:34:03 +00:00
async def get_updates ( self , include_posts = True ) :
2020-09-17 12:57:12 +00:00
# get any parameters we need
2020-09-20 12:43:34 +00:00
# post_ids_read = list(self.inbox_read_db.values)
2020-09-21 08:58:57 +00:00
if not self . pubkey :
return { ' success ' : False , ' status ' : ' Cannot log into this user whose public key I do not have ' }
if not self . privkey :
return { ' success ' : False , ' status ' : ' Cannot log into this user whose private key I do not have ' }
2020-09-17 12:57:12 +00:00
# compose msg
msg_to_op = {
2020-09-17 12:59:44 +00:00
* * self . login_details ,
2020-09-17 12:57:12 +00:00
}
2020-09-17 13:00:14 +00:00
self . log ( ' msg to op -> ' , msg_to_op )
2020-09-17 12:57:12 +00:00
2020-09-22 19:34:03 +00:00
res = await self . ring_ring (
2020-09-17 12:57:12 +00:00
msg_to_op ,
2020-09-17 12:23:11 +00:00
route = ' get_updates '
2020-09-17 10:37:58 +00:00
)
2020-09-17 12:23:11 +00:00
self . log ( ' <- Op.get_updates <- ' , res )
2020-09-15 09:12:11 +00:00
2020-09-17 12:23:11 +00:00
# error?
if not res . get ( ' success ' ) : return res
2020-09-15 10:59:19 +00:00
2020-09-17 12:23:11 +00:00
# (2) save msgs
2020-09-17 13:43:25 +00:00
id2msg = res . get ( ' res_msgs ' ) . get ( ' posts ' , { } )
2020-09-16 14:34:21 +00:00
2020-09-17 12:23:11 +00:00
# (3) save posts
2020-09-18 08:32:35 +00:00
if include_posts :
id2post = res . get ( ' res_posts ' ) . get ( ' posts ' , { } )
2020-09-17 19:13:51 +00:00
2020-09-18 10:19:34 +00:00
# save them: but posts arent msgs!
# @hack! why is this happening?
id2msg = dict ( [ ( k , v ) for k , v in id2msg . items ( ) if k not in id2post ] )
2020-09-17 19:13:51 +00:00
self . log ( f ' downloaded { len ( id2msg ) } messages: ' , list ( id2msg . keys ( ) ) )
2020-09-18 08:32:35 +00:00
if include_posts :
self . log ( f ' downloaded { len ( id2post ) } posts: ' , list ( id2post . keys ( ) ) )
2020-09-17 19:13:51 +00:00
self . save_msgs ( id2msg )
2020-09-18 08:32:35 +00:00
if include_posts :
self . save_posts ( id2post )
2020-09-17 13:09:11 +00:00
2020-09-17 19:16:29 +00:00
# return {
# 'status':f'Retrieved {len(id2post)} posts and {len(id2msg)} messages.',
# 'id2msg':id2msg,
# 'id2post':id2post
# }
2020-09-19 15:12:11 +00:00
#res['status']=''
#if len(id2post) or len(id2msg):
# res['status']=f'You\'ve got {len(id2post)} new posts and {len(id2msg)} new messages.'
2020-09-19 15:16:06 +00:00
_nup = self . num_unread_posts
_num = self . num_unread_msgs
res [ ' status ' ] = f ''' You have { _nup } unseen post { ' s ' if _nup != 1 else ' ' } and { _num } unread msg { ' s ' if _num != 1 else ' ' } . '''
2020-09-19 15:12:11 +00:00
2020-09-17 13:09:11 +00:00
return res
2020-09-15 06:39:11 +00:00
2020-09-19 15:16:26 +00:00
@property
2020-09-19 15:16:06 +00:00
def num_unread_posts ( self ) :
return len ( self . posts ( unread = True ) )
2020-09-19 15:16:26 +00:00
@property
2020-09-19 15:16:06 +00:00
def num_posts ( self ) :
return len ( self . posts ( ) )
2020-09-19 15:16:26 +00:00
@property
2020-09-19 15:16:06 +00:00
def num_unread_msgs ( self ) :
return len ( self . messages ( unread = True ) )
2020-09-19 15:16:26 +00:00
@property
2020-09-19 15:16:06 +00:00
def num_msgs ( self ) :
return len ( self . messages ( ) )
2020-09-17 06:19:41 +00:00
2020-09-19 15:56:15 +00:00
def posts ( self ,
unread = None ,
inbox_prefix = ' /inbox/ ' ) :
2020-09-18 12:45:24 +00:00
inbox_prefix = ' /feed/ '
2020-09-19 15:56:15 +00:00
inbox = self . get_inbox_crypt ( prefix = inbox_prefix ) . values
read = self . get_inbox_crypt ( prefix = inbox_prefix + ' read/ ' ) . values
self . log ( ' post index<- ' , inbox )
self . log ( ' post index read<- ' , read )
if unread :
inbox = [ x for x in inbox if not x in set ( read ) ]
2020-09-18 12:45:24 +00:00
posts = [ ]
2020-09-19 15:56:15 +00:00
for post_id in inbox :
2020-09-18 12:45:24 +00:00
self . log ( ' ??? ' , post_id , inbox_prefix )
2020-09-20 19:34:45 +00:00
try :
res_post = self . read_post ( post_id )
except ThemisError as e :
self . log ( ' !! ' , e )
continue
2020-09-18 12:45:24 +00:00
self . log ( ' got post: ' , res_post )
if res_post . get ( ' success ' ) and res_post . get ( ' post ' ) :
post = res_post . get ( ' post ' )
post . post_id = post_id
2020-09-19 15:31:40 +00:00
post . inbox_prefix = inbox_prefix
2020-09-18 12:45:24 +00:00
posts . append ( post )
return posts
def read_post ( self , post_id , post_encr = None ) :
# get post
if not post_encr :
post_encr = self . crypt_data . get (
post_id ,
prefix = ' /post/ '
)
self . log ( ' found encrypted post store: ' , post_encr )
# first from op to me?
try :
msg_from_op_b_encr = post_encr
self . log ( ' !?!? ' , self . name , self . uri , post_id , ' \n ' , self . privkey )
msg_from_op_b = SMessage (
self . privkey . data ,
self . op . pubkey . data
) . unwrap ( msg_from_op_b_encr )
self . log ( ' decrypted?? ' , msg_from_op_b )
except ( ThemisError , TypeError ) as e :
self . log ( f ' !!!!! { e } !!!!! ' )
return {
' success ' : False ,
' status ' : ' Could not decrypt from operator. '
}
# decoded?
msg_from_op = pickle . loads ( msg_from_op_b )
self . log ( ' decoded? ' , msg_from_op )
post_signed_b = msg_from_op . get ( ' post ' )
post_from_uri = msg_from_op . get ( ' post_from ' )
post_from_name = msg_from_op . get ( ' post_from_name ' )
2020-09-28 19:17:38 +00:00
post_timestamp = msg_from_op . get ( ' post_timestamp ' )
2020-09-18 12:45:24 +00:00
# verify!
try :
post_b = sverify (
b64dec ( post_from_uri ) ,
post_signed_b
)
except ThemisError :
return { ' success ' : False , ' status ' : ' Verification failed ' }
# decode
post_data = pickle . loads ( post_b )
# pretend its a message to world?
post_obj = Message (
{
' from ' : post_from_uri , ' from_name ' : post_from_name ,
' to ' : Komrade ( WORLD_NAME ) . uri , ' to_name ' : WORLD_NAME ,
2020-09-28 19:17:38 +00:00
' msg ' : post_data ,
' timestamp ' : post_timestamp
2020-09-18 12:45:24 +00:00
}
2020-09-17 19:57:07 +00:00
)
2020-09-18 12:45:24 +00:00
self . log ( ' post obj? ' , post_obj )
post_obj . post_id = post_id
return {
' success ' : True ,
' post ' : post_obj
}
2020-09-17 19:57:07 +00:00
2020-09-29 09:19:02 +00:00
def msgs ( self , * x , * * y ) : return self . messages ( * x , * * y )
2020-09-17 19:57:07 +00:00
def messages ( self ,
2020-09-19 15:12:11 +00:00
unread = None ,
2020-09-17 19:57:07 +00:00
inbox_prefix = ' /inbox/ ' ) :
2020-09-17 12:23:11 +00:00
# meta inbox
2020-09-17 19:57:07 +00:00
self . log ( ' <-- ' , inbox_prefix , ' ??? ' )
inbox_db = self . get_inbox_crypt ( prefix = inbox_prefix )
read_db = self . get_inbox_crypt ( prefix = inbox_prefix + ' read/ ' )
inbox = inbox_db . values
read = read_db . values
2020-09-17 16:37:59 +00:00
self . log ( ' <- inbox ' , inbox )
2020-09-17 19:57:07 +00:00
self . log ( ' <- read ' , read )
2020-09-17 16:37:59 +00:00
2020-09-17 12:23:11 +00:00
# filter?
2020-09-19 15:12:11 +00:00
if unread :
inbox = [ x for x in inbox if not x in set ( read ) ]
2020-09-17 12:57:12 +00:00
2020-09-17 12:23:11 +00:00
# decrypt and read all posts
2020-09-17 16:37:59 +00:00
msgs = [ ]
for post_id in inbox :
2020-09-19 15:12:11 +00:00
# self.log('???',post_id,inbox_prefix)
2020-09-17 17:01:17 +00:00
res_msg = self . read_msg ( post_id )
2020-09-19 15:12:11 +00:00
# self.log('got msg:',res_msg)
2020-09-17 17:01:17 +00:00
if res_msg . get ( ' success ' ) and res_msg . get ( ' msg ' ) :
2020-09-17 17:33:32 +00:00
msgx = res_msg . get ( ' msg ' )
msgx . post_id = post_id
2020-09-19 15:31:40 +00:00
msgx . inbox_prefix = inbox_prefix
2020-09-17 17:33:32 +00:00
msgs . append ( msgx )
2020-09-17 12:23:11 +00:00
return msgs
2020-09-19 15:31:40 +00:00
def seen_msg ( self , msg_or_post ) :
# read_msgs = self.get_inbox_crypt(
# uri=self.uri,
# prefix=msg_or_post.inbox_prefix + 'read/'
# )
inbox_prefix = msg_or_post . inbox_prefix
self . log ( ' inbox_prefix ' , inbox_prefix )
read_db = self . get_inbox_crypt ( prefix = inbox_prefix + ' read/ ' )
2020-09-19 15:32:45 +00:00
self . log ( ' read_db.values 1 ' , read_db . values )
2020-09-19 15:31:40 +00:00
read_db . append ( msg_or_post . post_id )
2020-09-19 15:32:45 +00:00
self . log ( ' read_db.values 2 ' , read_db . values )
2020-09-19 15:31:40 +00:00
2020-09-17 12:23:11 +00:00
def read_msg ( self , post_id = None , post_encr = None ) :
2020-09-15 06:39:11 +00:00
# get post
2020-09-15 06:40:32 +00:00
if not post_encr :
2020-09-17 16:42:20 +00:00
post_encr = self . crypt_data . get (
post_id ,
prefix = ' /post/ '
)
2020-09-15 07:04:01 +00:00
self . log ( ' found encrypted post store: ' , post_encr )
2020-09-17 12:23:11 +00:00
2020-09-17 16:42:20 +00:00
# first from op to me?
2020-09-17 17:20:50 +00:00
try :
msg_from_op_b_encr = post_encr
2020-09-18 05:51:34 +00:00
self . log ( ' !?!? ' , self . name , self . uri , post_id , ' \n ' , self . privkey )
2020-09-17 17:20:50 +00:00
msg_from_op_b = SMessage (
self . privkey . data ,
2020-09-18 06:36:12 +00:00
self . op . pubkey . data
2020-09-17 17:29:18 +00:00
) . unwrap ( msg_from_op_b_encr )
2020-09-17 17:20:50 +00:00
self . log ( ' decrypted?? ' , msg_from_op_b )
2020-09-17 17:32:08 +00:00
except ( ThemisError , TypeError ) as e :
2020-09-17 17:20:50 +00:00
self . log ( f ' !!!!! { e } !!!!! ' )
return {
' success ' : False ,
' status ' : ' Could not decrypt from operator. '
}
2020-09-15 06:39:11 +00:00
2020-09-17 16:42:20 +00:00
# decoded?
msg_from_op = pickle . loads ( msg_from_op_b )
self . log ( ' decoded? ' , msg_from_op )
2020-09-14 22:00:13 +00:00
2020-09-17 16:42:20 +00:00
self . log ( ' msg_from_op is now ' , msg_from_op )
2020-09-15 08:27:02 +00:00
# this really to me?
2020-09-17 18:35:18 +00:00
if msg_from_op . get ( ' to_name ' ) != self . name :
if msg_from_op . get ( ' to_name ' ) == WORLD_NAME :
return {
' success ' : True ,
' msg ' : Message ( msg_from_op )
}
else :
2020-09-18 13:02:12 +00:00
# print(msg_from_op)
raise KomradeException ( ' Why am I getting this msg? ' + str ( msg_from_op ) )
2020-09-17 18:35:18 +00:00
return
2020-09-15 08:27:02 +00:00
# now try to decrypt?
msg2me = Message (
to_whom = self ,
msg_d = {
2020-09-17 17:01:17 +00:00
' from ' : msg_from_op . get ( ' from ' ) ,
' from_name ' : msg_from_op . get ( ' from_name ' ) ,
' msg ' : msg_from_op . get ( ' msg ' )
2020-09-15 08:27:02 +00:00
}
)
2020-09-17 17:01:17 +00:00
self . log ( ' msg2me is now v1 ' , msg2me , msg2me . is_encrypted , msg2me . has_embedded_msg )
if not msg2me . is_encrypted :
return {
' success ' : True ,
' msg ' : msg2me
}
2020-09-17 16:30:26 +00:00
try :
msg2me . decrypt ( )
self . log ( ' msg2me is now v2 ' , dict_format ( msg2me . msg_d ) )
except ThemisError as e :
2020-09-17 17:01:17 +00:00
self . log ( ' decryption failuire!!! ' )
2020-09-17 16:30:26 +00:00
return {
' success ' : False ,
' status ' : f ' De/encryption failure: { e } '
}
2020-09-15 08:27:02 +00:00
2020-09-17 17:29:18 +00:00
msg2me . post_id = post_id
2020-09-15 11:45:19 +00:00
return {
' success ' : True ,
' msg ' : msg2me
}
2020-09-15 08:27:02 +00:00
2020-09-15 11:12:49 +00:00
2020-09-16 14:34:21 +00:00
2020-09-15 11:12:49 +00:00
2020-09-17 10:37:58 +00:00
2020-09-17 12:23:11 +00:00
2020-09-17 10:37:58 +00:00
2020-09-15 11:12:49 +00:00
2020-09-10 14:00:55 +00:00
def test_register ( ) :
import random
num = random . choice ( list ( range ( 0 , 1000 ) ) )
2020-09-10 21:32:59 +00:00
botname = f ' marx { str ( num ) . zfill ( 3 ) } '
2020-09-13 07:15:44 +00:00
marxbot = Komrade ( botname )
# marxbot=Komrade()
2020-09-14 06:02:17 +00:00
marxbot . register ( passphrase = ' boo ' )
2020-09-09 15:30:34 +00:00
2020-09-13 19:42:04 +00:00
2020-09-13 20:24:31 +00:00
2020-09-13 19:42:04 +00:00
def test_msg ( ) :
2020-09-15 06:10:22 +00:00
b = Komrade ( ' bez ' )
2020-09-15 07:04:01 +00:00
b . inbox ( )
# b.read_msg(b'YWY3NDUxZjNjYjdhNDFmNmIyNDI2NzU3YTI4ZTA0OWM=')
2020-09-15 06:40:03 +00:00
#b.login()
2020-09-15 06:10:22 +00:00
2020-09-15 06:40:03 +00:00
#print(b.download_msgs())
2020-09-15 06:10:22 +00:00
# z = Komrade('zuckbot')
2020-09-13 20:24:31 +00:00
2020-09-15 06:10:22 +00:00
# b.msg(z,'you ssssssuck')
2020-09-13 20:24:31 +00:00
2020-09-13 19:42:04 +00:00
2020-09-14 07:58:26 +00:00
def test_loading ( ) :
2020-09-14 19:25:24 +00:00
z1 = Komrade ( ' elon ' )
2020-09-14 11:52:56 +00:00
# z1.register()
2020-09-14 08:25:55 +00:00
print ( z1 . keychain ( ) )
2020-09-14 11:52:56 +00:00
# exit()
2020-09-14 07:58:26 +00:00
z2 = Komrade ( b ' VUVDMgAAAC08BCMVA+0dMJXc66/W7hty669+3/3S61Q1yjmgJW8I0k3lqfDi ' )
print ( z2 )
print ( z2 . keychain ( ) )
2020-09-08 15:14:48 +00:00
2020-09-14 07:58:26 +00:00
pprint ( PHONEBOOK )
return
# z1.login()
2020-09-09 15:41:33 +00:00
2020-09-15 06:10:22 +00:00
def test_sign ( ) :
from pythemis import smessage
b = Komrade ( ' bez ' )
m = Komrade ( ' marx ' )
z = Komrade ( ' zuckbot ' )
msg = b ' this is cool. --bez '
signed_msg = smessage . ssign ( b . privkey . data , msg )
print ( signed_msg )
verified_msg = smessage . sverify ( b . pubkey . data , signed_msg )
print ( verified_msg )
2020-09-15 06:18:34 +00:00
def test_send ( ) :
z = Komrade ( ' zuckbot ' )
2020-09-09 17:39:30 +00:00
2020-09-14 07:58:26 +00:00
if __name__ == ' __main__ ' :
2020-09-15 06:10:22 +00:00
test_msg ( )