mirror of
https://github.com/ComradCollective/Comrad
synced 2024-11-05 21:20:51 +00:00
moving old api code out for now
This commit is contained in:
parent
b12dca60ca
commit
63f92b18f0
@ -1,513 +0,0 @@
|
||||
# ### Constants
|
||||
from komrade import *
|
||||
LAST_N_IN_INBOX = 10
|
||||
|
||||
|
||||
### Imports
|
||||
|
||||
import os,time,sys,logging
|
||||
from pathlib import Path
|
||||
import asyncio,time,sys
|
||||
from base64 import b64encode,b64decode
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
import logging
|
||||
import asyncio
|
||||
import shelve
|
||||
from collections import OrderedDict
|
||||
import pickle,os
|
||||
from threading import Thread
|
||||
from pathlib import Path
|
||||
|
||||
# local imports
|
||||
from komrade import *
|
||||
CACHE_DIR = os.path.join(os.path.expanduser('~'),'.komrade','.cache')
|
||||
if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
|
||||
MEMCACHE_FNFN=os.path.join(CACHE_DIR,'.memory')
|
||||
|
||||
|
||||
### Logging
|
||||
def logger():
|
||||
import logging
|
||||
handler = logging.StreamHandler()
|
||||
formatter = logging.Formatter('[%(asctime)s]\n%(message)s\n')
|
||||
handler.setFormatter(formatter)
|
||||
logger = logging.getLogger('komrade')
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
return logger
|
||||
|
||||
LOG = None
|
||||
|
||||
def log(*x):
|
||||
global LOG
|
||||
if not LOG: LOG=logger().debug
|
||||
|
||||
tolog=' '.join(str(_) for _ in x)
|
||||
LOG(tolog)
|
||||
|
||||
|
||||
|
||||
## func
|
||||
|
||||
def bytes_from_file(filename,chunksize=8192):
|
||||
with open(filename, 'rb') as f:
|
||||
while True:
|
||||
piece = f.read(chunksize)
|
||||
if not piece:
|
||||
break
|
||||
yield piece
|
||||
|
||||
def get_random_id():
|
||||
import uuid
|
||||
return uuid.uuid4().hex
|
||||
|
||||
def get_random_binary_id():
|
||||
import base64
|
||||
idstr = get_random_id()
|
||||
return base64.b64encode(idstr.encode())
|
||||
|
||||
### Headless API
|
||||
|
||||
def boot_lonely_selfless_node(port=8467):
|
||||
async def go():
|
||||
api = Api(log=log, port=port)
|
||||
await api.connect_forever()
|
||||
asyncio.run(go())
|
||||
|
||||
|
||||
class NetworkStillConnectingError(OSError): pass
|
||||
|
||||
|
||||
|
||||
async def _getdb(self=None,port=PORT_LISTEN):
|
||||
from kademlia.network import Server
|
||||
|
||||
if self:
|
||||
self.log('starting server on port %s..' % port)
|
||||
|
||||
import os
|
||||
if self: self.log(os.getcwd())
|
||||
node = Server(log=self.log if self else None) #fn='../p2p/data.db',log=(self.log if self else print)))
|
||||
|
||||
try:
|
||||
if self: self.log('listening on port %s...' % format(port))
|
||||
await node.listen(port)
|
||||
except OSError:
|
||||
raise NetworkStillConnectingError('Still connecting...')
|
||||
#await asyncio.sleep(3)
|
||||
|
||||
if self: self.log('bootstrapping server..')
|
||||
await node.bootstrap(NODES_PRIME)
|
||||
|
||||
if self: node.log = self.log
|
||||
self.log('NODE:',node)
|
||||
|
||||
# if self and self.app:
|
||||
# self.app.close_dialog()
|
||||
|
||||
return node
|
||||
|
||||
def logg(*x):
|
||||
print(*x)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Api(object):
|
||||
def __init__(self,log=None,port=PORT_LISTEN):
|
||||
self.log = log if log is not None else logg
|
||||
self.port=port
|
||||
|
||||
# load file-based keys
|
||||
self.load_keys()
|
||||
|
||||
|
||||
async def connect_forever(self,save_every=60):
|
||||
try:
|
||||
i = 0
|
||||
self._node = await self.connect()
|
||||
while True:
|
||||
if not i%90: self.log(f'Node status (tick {i}): {self._node}')
|
||||
if i and not i%save_every: await self.flush()
|
||||
i += 1
|
||||
await asyncio.sleep(NODE_SLEEP_FOR)
|
||||
# asyncio.sleep(0)
|
||||
except (asyncio.CancelledError,KeyboardInterrupt) as e:
|
||||
self.log('P2P node cancelled', e)
|
||||
await self.flush()
|
||||
finally:
|
||||
# when canceled, print that it finished
|
||||
self.log('P2P node shutting down')
|
||||
pass
|
||||
|
||||
@property
|
||||
async def node(self):
|
||||
if not hasattr(self,'_node'):
|
||||
await self.connect()
|
||||
self._node.log=self.log
|
||||
return self._node
|
||||
|
||||
async def connect(self):
|
||||
port=self.port
|
||||
# if self.app: self.app.open_dialog('hello?')
|
||||
self.log('connecting on port %s...' % port)
|
||||
node = await _getdb(self,port)
|
||||
self.log(f'connect() has node {node}')
|
||||
self._node = node
|
||||
return node
|
||||
|
||||
def get_tor_python_session(self):
|
||||
# from torpy.http.requests import TorRequests
|
||||
# with TorRequests() as tor_requests:
|
||||
# with tor_requests.get_session() as s:
|
||||
# # return s
|
||||
# from torpy.http.requests import tor_requests_session
|
||||
# with tor_requests_session() as s: # returns requests.Session() object
|
||||
# return s
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def get_tor_proxy_session(self):
|
||||
session = requests.session()
|
||||
# Tor uses the 9050 port as the default socks port
|
||||
session.proxies = {'http': 'socks5://127.0.0.1:9050',
|
||||
'https': 'socks5://127.0.0.1:9050'}
|
||||
return session
|
||||
|
||||
def get_async_tor_proxy_session(self):
|
||||
from requests_futures.sessions import FuturesSession
|
||||
session = FuturesSession()
|
||||
# Tor uses the 9050 port as the default socks port
|
||||
session.proxies = {'http': 'socks5://127.0.0.1:9050',
|
||||
'https': 'socks5://127.0.0.1:9050'}
|
||||
return session
|
||||
|
||||
|
||||
|
||||
|
||||
def tor_request(self,url,method='get',data=None):
|
||||
stopfixthis
|
||||
with self.get_tor_proxy_session() as s:
|
||||
if method=='get':
|
||||
return s.get(url)
|
||||
elif method=='post':
|
||||
self.log('data',data)
|
||||
return s.post(url,data=data)
|
||||
|
||||
|
||||
def request(self,Q,**kwargs):
|
||||
self.log('request() Q:',Q)
|
||||
res = self.tor_request(Q,**kwargs)
|
||||
self.log('reqeust() <-',res)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
#@property
|
||||
def load_keys(self):
|
||||
# get key names
|
||||
pub_key_names = [x.split('.')[1] for x in os.listdir(KEY_PATH_PUB) if x.count('.')==2 and x.endswith('.loc')]
|
||||
priv_key_names = [x.split('.')[1] for x in os.listdir(KEY_PATH_PRIV) if x.count('.')==2 and x.endswith('.key')]
|
||||
key_names = set(pub_key_names)|set(priv_key_names)
|
||||
|
||||
self.log('get_keys() found public key names:',pub_key_names)
|
||||
self.log('get_keys() found private key names:',priv_key_names)
|
||||
|
||||
|
||||
# load and find all local
|
||||
self._keys = {}
|
||||
for key_name in key_names:
|
||||
self.log('key_name =',key_name)
|
||||
self._keys[key_name] = Komrade(key_name,api=self,create_if_missing=False)
|
||||
|
||||
# break into types
|
||||
self.accounts = [self._keys[name] for name in priv_key_names]
|
||||
self.contacts = [self._keys[name] for name in pub_key_names]
|
||||
|
||||
|
||||
self.log('get_keys() loaded accounts:',self.accounts)
|
||||
self.log('get_keys() loaded contacts:',self.contacts)
|
||||
|
||||
@property
|
||||
def keys(self):
|
||||
if not hasattr(self,'_keys'): self.load_keys()
|
||||
return self._keys
|
||||
|
||||
#async
|
||||
def personate(self,persona_name,create_if_missing=True):
|
||||
komrade = Komrade(persona_name,api=self,create_if_missing=create_if_missing)
|
||||
res = persona.boot()
|
||||
self.log('personate() res =',res)
|
||||
return persona
|
||||
# komrade = self.keys[persona_name] if persona_name in self.keys else None
|
||||
# if komrade is None and create_if_missing:
|
||||
# self.keys[persona_name] = komrade = Komrade(persona_name, api=self, create_if_missing=create_if_missing)
|
||||
# res = await persona.boot()
|
||||
# self.log('BOOT RESULT:',res)
|
||||
# return persona
|
||||
|
||||
|
||||
async def upload(self,filename,file_id=None, uri='/file/',uri_part='/part/'):
|
||||
import sys
|
||||
|
||||
if not file_id: file_id = get_random_id()
|
||||
part_ids = []
|
||||
part_keys = []
|
||||
parts=[]
|
||||
PARTS=[]
|
||||
buffer_size=100
|
||||
for part in bytes_from_file(filename,chunksize=1024*2):
|
||||
part_id = get_random_id()
|
||||
part_ids.append(part_id)
|
||||
part_key='/part/'+part_id
|
||||
part_keys.append(part_key)
|
||||
parts.append(part)
|
||||
# PARTS.append(part)
|
||||
|
||||
# self.log('part!:',sys.getsizeof(part))
|
||||
#self.set(part_key,part)
|
||||
|
||||
if len(parts)>=buffer_size:
|
||||
# self.log('setting...')
|
||||
await self.set(part_keys,parts)
|
||||
part_keys=[]
|
||||
PARTS+=parts
|
||||
parts=[]
|
||||
|
||||
# set all parts
|
||||
#self.set(part_keys,PARTS)
|
||||
# self.log('# parts:',len(PARTS))
|
||||
if parts and part_keys:
|
||||
await self.set(part_keys, parts)
|
||||
|
||||
# how many parts?
|
||||
# self.log('# pieces!',len(part_ids))
|
||||
|
||||
file_store = {'ext':os.path.splitext(filename)[-1][1:], 'parts':part_ids}
|
||||
# self.log('FILE STORE??',file_store)
|
||||
await self.set_json(uri+file_id,file_store)
|
||||
|
||||
# file_store['data'].seek(0)
|
||||
file_store['id']=file_id
|
||||
return file_store
|
||||
|
||||
async def download(self,file_id):
|
||||
self.log('file_id =',file_id)
|
||||
file_store = await self.get_json_val('/file/'+file_id)
|
||||
self.log('file_store =',file_store)
|
||||
if file_store is None: return
|
||||
|
||||
self.log('file_store!?',file_store)
|
||||
keys = ['/part/'+x for x in file_store['parts']]
|
||||
|
||||
#time,pieces,pub,sign = await self.get_json_val(keys)
|
||||
pieces = await self.get_json_val(keys)
|
||||
self.log('pieces = ',pieces)
|
||||
file_store['parts_data']=pieces
|
||||
return file_store
|
||||
|
||||
async def flush(self):
|
||||
#self.log('saving back to db file...')
|
||||
node = await self.node
|
||||
node.storage.dump()
|
||||
# self.log('DONE saving back to db file...')
|
||||
|
||||
|
||||
|
||||
async def get_posts(self,uri='/inbox/world'):
|
||||
# get IDs
|
||||
post_ids = await self.get_post_ids(uri)
|
||||
|
||||
# get posts
|
||||
posts = [self.get_post(post_id) for post_id in post_ids]
|
||||
return await asyncio.gather(*posts)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async def read_inbox(self,uri_inbox=None):
|
||||
if uri_inbox is None: uri_inbox = P2P_PREFIX_INBOX+self.name.encode()
|
||||
node = await self.node
|
||||
inbox_ids = await node.get(uri_inbox)
|
||||
if inbox_ids is not None:
|
||||
inbox_ids = inbox_ids.split(BSEP)
|
||||
self.log('found inbox IDs:',inbox_ids)
|
||||
|
||||
msgs_toread = [self.read_msg(msg_id) for msg_id in inbox_ids]
|
||||
msgs = await asyncio.gather(*msgs_toread)
|
||||
self.log('read_inbox() msgs = ',msgs)
|
||||
return msgs
|
||||
return []
|
||||
|
||||
async def read_outbox(self,uri_outbox=None):
|
||||
if uri_outbox is None: uri_outbox = P2P_PREFIX_OUTBOX+self.name.encode()
|
||||
return await self.read_inbox(uri_outbox)
|
||||
|
||||
|
||||
async def read_msg(self,msg_id):
|
||||
self.log(f'Persona.read_msg({msg_id}) ?')
|
||||
uri_msg=P2P_PREFIX_POST+msg_id
|
||||
node = await self.node
|
||||
komrade = await self.komrade
|
||||
|
||||
res = await node.get(uri_msg)
|
||||
self.log('res = ',res)
|
||||
if res is not None:
|
||||
double_encrypted_payload_b64 = res
|
||||
single_encrypted_payload = self.decrypt(double_encrypted_payload_b64, komrade.pubkey_b64)
|
||||
self.log('GOT ENRYPTED PAYLOAD:',single_encrypted_payload)
|
||||
|
||||
signed_encrypted_payload_b64,from_pubkey_b64,name_b64,time_b64 = single_encrypted_payload.split(BSEP)
|
||||
self.log('signed_encrypted_payload =',signed_encrypted_payload_b64)
|
||||
self.log('from_pubkey_b64 =',from_pubkey_b64)
|
||||
self.log('time_b64 =',time_b64)
|
||||
|
||||
from_name = b64decode(name_b64).decode()
|
||||
self.log('from_name =',from_name)
|
||||
timestamp = b64decode(time_b64).decode()
|
||||
tmpP = Komrade(from_name)
|
||||
await tmpP.boot()
|
||||
from_pubkey_b64_accto_name = tmpP.pubkey_b64
|
||||
assert from_pubkey_b64==from_pubkey_b64_accto_name
|
||||
|
||||
encrypted_payload_b64 = self.verify(signed_encrypted_payload_b64, from_pubkey_b64)
|
||||
self.log('encrypted_payload_b64 =',encrypted_payload_b64)
|
||||
|
||||
payload = self.decrypt(encrypted_payload_b64, from_pubkey_b64)
|
||||
self.log('payload =',payload)
|
||||
return {
|
||||
'success':True,
|
||||
'content':payload,
|
||||
'from_name':from_name,
|
||||
'from_pubkey_b64':from_pubkey_b64,
|
||||
'timestamp':timestamp
|
||||
}
|
||||
return {'error':'Unknown'}
|
||||
|
||||
async def refresh_inboxes(self):
|
||||
uris_to_get=[]
|
||||
|
||||
for komrade in self.accounts:
|
||||
inbox = await persona.load_inbox(decrypt_msg_uri=True, last=LAST_N_IN_INBOX)
|
||||
for decr_msg_uri in inbox:
|
||||
uris_to_get.append(self.get_msg(decr_msg_uri))
|
||||
# uris_to_get+=inbox
|
||||
|
||||
|
||||
res = await asyncio.gather(*uris_to_get)
|
||||
for decr_msg_uri,encr_msg in res:
|
||||
self.memcache[decr_msg_uri]=encr_msg
|
||||
|
||||
self.memcache_save()
|
||||
|
||||
@property
|
||||
def memcache(self):
|
||||
if not hasattr(self,'_memcache'):
|
||||
self._memcache = OrderedDict()
|
||||
if os.path.exists(MEMCACHE_FNFN):
|
||||
import pickle
|
||||
try:
|
||||
self._memcache = pickle.load(open(MEMCACHE_FNFN,'rb'))
|
||||
except EOFError:
|
||||
pass
|
||||
return self._memcache
|
||||
|
||||
def memcache_save(self):
|
||||
import pickle
|
||||
with open(MEMCACHE_FNFN,'wb') as of:
|
||||
pickle.dump(self.memcache, of)
|
||||
self.log('>> saved:',MEMCACHE_FNFN)
|
||||
|
||||
async def get_msg(self,decr_msg_uri):
|
||||
self.log('get_msg()',decr_msg_uri)
|
||||
rval=self.memcache.get(decr_msg_uri)
|
||||
self.log('got <--',rval)
|
||||
if rval is not None:
|
||||
self.log('in memcache')
|
||||
encr_msg = rval
|
||||
else:
|
||||
self.log('>> downloading',decr_msg_uri,'...')
|
||||
|
||||
node = await self.node
|
||||
encr_msg = await node.get(decr_msg_uri)
|
||||
self.log('downloaded:',encr_msg)
|
||||
|
||||
return (decr_msg_uri,encr_msg)
|
||||
#self.memcache.
|
||||
|
||||
async def see(self,decr_msg_id):
|
||||
res=await self.get(decr_msg_id)
|
||||
self.log('see() saw',res)
|
||||
return decr_msg_id
|
||||
|
||||
|
||||
|
||||
async def test1():
|
||||
api = Api()
|
||||
|
||||
marx=await api.personate('marx')
|
||||
elon=await api.personate('elon')
|
||||
res = await marx.send(b'secret',to=elon)
|
||||
|
||||
print(marx,elon,res)
|
||||
|
||||
async def test():
|
||||
|
||||
api = Api()
|
||||
|
||||
|
||||
# message?
|
||||
marx=await api.personate('marx')
|
||||
elon=await api.personate('elon')
|
||||
res = await marx.send(b'secret',to=elon)
|
||||
|
||||
res = await elon.send(b'secret back',to=marx)
|
||||
# print(marx,elon,res)
|
||||
|
||||
# get overall inbox
|
||||
meta_inbox = await api.refresh_inboxes()
|
||||
api.log('meta_inbox',meta_inbox)
|
||||
|
||||
keys = api.memcache.keys()
|
||||
api.log('ALL KEYS =',keys)
|
||||
|
||||
for key in keys:
|
||||
val = api.memcache.get(key)
|
||||
api.log(key,'-->',val)
|
||||
# stop
|
||||
|
||||
#async
|
||||
async def test_keyserver():
|
||||
api = Api()
|
||||
marx = api.personate('marx')
|
||||
elon = api.personate('elon')
|
||||
|
||||
|
||||
|
||||
|
||||
zuck = api.personate('zuck')
|
||||
|
||||
print('marx',marx.pubkey_b64)
|
||||
|
||||
print('elon',elon.pubkey_b64)
|
||||
|
||||
print('zuck',zuck.pubkey_b64)
|
||||
|
||||
|
||||
#marx = await api.personate(marx)
|
||||
#res = await api.get_externally_signed_pubkey('marx')
|
||||
#res = await api.get_externally_signed_pubkey('marx')
|
||||
#return res
|
||||
|
||||
await elon.send(b'oh no',to=marx)
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
asyncio.run(test_keyserver())
|
@ -1,81 +0,0 @@
|
||||
# mine imports
|
||||
import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')),'..')))
|
||||
from komrade.backend.caller import Caller
|
||||
from komrade import KomradeException,Logger
|
||||
|
||||
# other imports
|
||||
import asyncio,os,time,sys,logging,getpass
|
||||
from pythemis.skeygen import KEY_PAIR_TYPE, GenerateKeyPair
|
||||
from pythemis.smessage import SMessage, ssign, sverify
|
||||
from pythemis.exception import ThemisError
|
||||
from pythemis.scell import SCellSeal
|
||||
from base64 import b64decode,b64encode
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class Model(Logger): pass
|
||||
|
||||
class UserAlreadyExists(KomradeException): pass
|
||||
|
||||
class Komrade(Model):
|
||||
def __init__(self, name, is_group=False):
|
||||
self.name = name
|
||||
self.is_group=is_group
|
||||
|
||||
@property
|
||||
def op(self):
|
||||
return Caller(self.name)
|
||||
|
||||
|
||||
###
|
||||
# MAJOR OPERATIONS
|
||||
###
|
||||
|
||||
def register(self,passphrase = DEBUG_DEFAULT_PASSPHRASE):
|
||||
"""
|
||||
Register this new persona.
|
||||
Protect keys according to a passphrase.
|
||||
If group, only admin key pass-protected;
|
||||
if individual, all keys pass-protected.
|
||||
"""
|
||||
|
||||
# Does user already exist?
|
||||
if self.op.exists(): raise UserAlreadyExists('User already exists')
|
||||
|
||||
# Get passphrase
|
||||
if not passphrase: passphrase = getpass.getpass('Enter password for new account: ')
|
||||
|
||||
# Create
|
||||
if self.is_group:
|
||||
self.op.create_keys(adminkey_pass=passphrase)
|
||||
else:
|
||||
self.op.create_keys(privkey_pass=passphrase,adminkey_pass=passphrase)
|
||||
|
||||
|
||||
def login(self,passphrase = DEBUG_DEFAULT_PASSPHRASE):
|
||||
# Get passphrase
|
||||
if not passphrase: passphrase = getpass.getpass('Enter login password: ')
|
||||
|
||||
# Get my decryption keys
|
||||
if self.is_group:
|
||||
keychain_decr = self.op.keychain_decr(adminkey_pass=passphrase)
|
||||
else:
|
||||
keychain_decr = self.op.keychain_decr(privkey_pass=passphrase,adminkey_pass=passphrase)
|
||||
|
||||
print(keychain_decr)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import random
|
||||
idnum = random.choice(list(range(1000)))
|
||||
komrade = Komrade('Op'+str(idnum))
|
||||
print('\n\n\nREGISTERING\n\n\n')
|
||||
persona.register(passphrase='bb')
|
||||
|
||||
print('\n\n\nLOGGING IN\n\n\n')
|
||||
|
||||
persona.login(passphrase='bb')
|
@ -1,739 +0,0 @@
|
||||
import os
|
||||
import asyncio
|
||||
from pythemis.skeygen import KEY_PAIR_TYPE, GenerateKeyPair
|
||||
from pythemis.smessage import SMessage, ssign, sverify
|
||||
from pythemis.exception import ThemisError
|
||||
from pythemis.scell import SCellSeal
|
||||
|
||||
from base64 import b64decode,b64encode
|
||||
# from kademlia.network import Server
|
||||
import os,time,sys,logging
|
||||
from pathlib import Path
|
||||
import requests
|
||||
|
||||
sys.path.append('../p2p')
|
||||
BSEP=b'||||||||||'
|
||||
BSEP2=b'@@@@@@@@@@'
|
||||
BSEP3=b'##########'
|
||||
NODE_SLEEP_FOR=1
|
||||
|
||||
P2P_PREFIX=b'/persona/'
|
||||
P2P_PREFIX_POST=b'/msg/'
|
||||
P2P_PREFIX_INBOX=b'/inbox/'
|
||||
P2P_PREFIX_OUTBOX=b'/outbox/'
|
||||
|
||||
WORLD_PUB_KEY = b'VUVDMgAAAC1z53KeApQY4RICK5k0nXnnS+K17veIFMPlFKo7mqnRhTZDhAmG'
|
||||
WORLD_PRIV_KEY = b'UkVDMgAAAC26HXeGACxZUoKYKlZ7sDmVoLwffNj3CrdqoPrE94+2ysfhufmP'
|
||||
|
||||
KOMRADE_PUB_KEY = b'VUVDMgAAAC09uo+wAgu/V9xyvMkMDbOQEk1ssOrFADaiyTzfwVjE6o8FHoil'
|
||||
KOMRADE_PRIV_KEY = b'UkVDMgAAAC33fFiaAIpmQewjkYndzMcMkj1mLy/lE4RXJQzIlUN94tyC5g29'
|
||||
|
||||
DEBUG = True
|
||||
UPLOAD_DIR = 'uploads/'
|
||||
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
|
||||
|
||||
PORT_LISTEN = 5639
|
||||
NODE_SLEEP_FOR=1
|
||||
NODES_PRIME = [("128.232.229.63",8467)]
|
||||
KEYSERVER_ADDR = 'komrade.app' #'128.232.229.63'
|
||||
KEYSERVER_PORT = 5566
|
||||
|
||||
|
||||
KEY_PATH = os.path.join(os.path.expanduser('~'),'.komrade')
|
||||
KEY_PATH_PUB = os.path.join(KEY_PATH,'.locs')
|
||||
KEY_PATH_PRIV = os.path.join(KEY_PATH,'.keys')
|
||||
|
||||
for x in [KEY_PATH,KEY_PATH_PUB,KEY_PATH_PRIV]:
|
||||
if not os.path.exists(x): os.makedirs(x)
|
||||
|
||||
WORLD_PRIV_KEY_FN = os.path.join(KEY_PATH_PRIV,'.world.key')
|
||||
WORLD_PUB_KEY_FN = os.path.join(KEY_PATH_PUB,'.world.loc')
|
||||
KOMRADE_PRIV_KEY_FN = os.path.join(KEY_PATH_PRIV,'.komrade.key')
|
||||
KOMRADE_PUB_KEY_FN = os.path.join(KEY_PATH_PUB,'.komrade.loc')
|
||||
|
||||
|
||||
def check_world_keys():
|
||||
if not os.path.exists(WORLD_PRIV_KEY_FN):
|
||||
with open(WORLD_PRIV_KEY_FN,'wb') as of:
|
||||
of.write(WORLD_PRIV_KEY)
|
||||
|
||||
if not os.path.exists(WORLD_PUB_KEY_FN):
|
||||
with open(WORLD_PUB_KEY_FN,'wb') as of:
|
||||
of.write(WORLD_PUB_KEY)
|
||||
|
||||
if not os.path.exists(KOMRADE_PRIV_KEY_FN):
|
||||
with open(KOMRADE_PRIV_KEY_FN,'wb') as of:
|
||||
of.write(KOMRADE_PRIV_KEY)
|
||||
|
||||
if not os.path.exists(KOMRADE_PUB_KEY_FN):
|
||||
with open(KOMRADE_PUB_KEY_FN,'wb') as of:
|
||||
of.write(KOMRADE_PUB_KEY)
|
||||
|
||||
# check_world_keys()
|
||||
|
||||
|
||||
## CONNECTING
|
||||
|
||||
|
||||
|
||||
|
||||
## utils
|
||||
|
||||
def get_random_id():
|
||||
import uuid
|
||||
return uuid.uuid4().hex
|
||||
|
||||
def get_random_binary_id():
|
||||
import base64
|
||||
idstr = get_random_id()
|
||||
return base64.b64encode(idstr.encode())
|
||||
|
||||
|
||||
def logger():
|
||||
import logging
|
||||
handler = logging.StreamHandler()
|
||||
formatter = logging.Formatter('[%(asctime)s]\n%(message)s\n')
|
||||
handler.setFormatter(formatter)
|
||||
logger = logging.getLogger(__file__)
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
return logger
|
||||
|
||||
LOG = None
|
||||
|
||||
def log(*x):
|
||||
global LOG
|
||||
if not LOG: LOG=logger().debug
|
||||
|
||||
tolog=' '.join(str(_) for _ in x)
|
||||
LOG(tolog)
|
||||
|
||||
|
||||
|
||||
class NetworkStillConnectingError(OSError): pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Komrade(object):
|
||||
|
||||
def __init__(self,name,api=None,node=None,create_if_missing=True):
|
||||
self.name=name
|
||||
self.log=log
|
||||
self.privkey=None
|
||||
self.pubkey=None
|
||||
self.api=api
|
||||
|
||||
self.can_receive = False
|
||||
self.can_send = False
|
||||
# self.can_login = False
|
||||
self.external_keys_loaded = False
|
||||
|
||||
|
||||
self.pubkey_enc = None
|
||||
self.pubkey_decr = None
|
||||
|
||||
# self._node=node
|
||||
self.create_if_missing=create_if_missing
|
||||
# self.log(f'>> Persona.__init__(name={name},create_if_missing={create_if_missing})')
|
||||
|
||||
# load at least any local keys (non-async)
|
||||
self.find_keys_local()
|
||||
# self.login_or_register()
|
||||
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
#pkeystr = '+loc' if self.has_public_key() else ''
|
||||
#privkeystr = ' +key' if self.has_private_key() else ''
|
||||
# ptypestr='acccount' if self.has_private_key() else 'contact'
|
||||
ptypestr='loc' if not self.has_private_key() else 'keyloc'
|
||||
|
||||
return f'{self.name} ({ptypestr})'
|
||||
|
||||
def get_keyserver_pubkey(self):
|
||||
Q=f'http://{KEYSERVER_ADDR}:{KEYSERVER_PORT}/pub'
|
||||
|
||||
r = self.api.request(Q)
|
||||
return r.content
|
||||
# self.log('r =',r,b64encode(r.content))
|
||||
# pubkey_b64 = b64encode(r.content)
|
||||
# return pubkey_b64
|
||||
|
||||
def get_externally_signed_pubkey(self):
|
||||
Q=f'http://{KEYSERVER_ADDR}:{KEYSERVER_PORT}/get/{self.name}'
|
||||
self.log('Q:',Q)
|
||||
r = self.api.request(Q)
|
||||
package_b64 = r.content
|
||||
package = b64decode(package_b64)
|
||||
self.log('package <--',package)
|
||||
if not package: return (b'',b'',b'')
|
||||
return package.split(BSEP)
|
||||
# pubkey_b64, signed_pubkey_b64, server_signed_pubkey_b64 = package.split(BSEP)
|
||||
|
||||
# signed_pubkey = b64encode(r.content)
|
||||
# return (b64encode(pubkey), b64encode(signed_pubkey))
|
||||
|
||||
def set_externally_signed_pubkey(self):
|
||||
import requests
|
||||
Q=f'http://{KEYSERVER_ADDR}:{KEYSERVER_PORT}/add/{self.name}' #/{name}/{key}
|
||||
|
||||
package = self.pubkey_b64 + BSEP + self.signed_pubkey_b64
|
||||
self.log('set_externally_signed_pubkey package -->',package)
|
||||
|
||||
r = requests.post(Q, data=package) #{'name':self.name,'key':self.pubkey_b64})
|
||||
return r
|
||||
|
||||
def has_private_key(self):
|
||||
return self.privkey is not None
|
||||
|
||||
def has_public_key(self):
|
||||
return self.pubkey is not None
|
||||
|
||||
|
||||
@property
|
||||
async def node(self):
|
||||
node = await self.api.node
|
||||
return node
|
||||
|
||||
|
||||
def load_keyserver_pubkey(self):
|
||||
self.keyserver_pubkey_b64 = self.get_keyserver_pubkey()
|
||||
self.keyserver_pubkey = b64decode(self.keyserver_pubkey_b64)
|
||||
self.log('keyserver_pubkey =',self.keyserver_pubkey)
|
||||
return self.keyserver_pubkey
|
||||
|
||||
def load_external_keys(self):
|
||||
if self.external_keys_loaded: return
|
||||
|
||||
self.pubkey_ext_b64, self.signed_pubkey_ext_b64, self.server_signed_pubkey_ext_b64 = self.get_externally_signed_pubkey()
|
||||
self.log('pubkey_ext_b64 =',self.pubkey_ext_b64)
|
||||
self.log('signed_pubkey_ext_b64 =',self.signed_pubkey_ext_b64)
|
||||
self.log('server_signed_pubkey_ext_b64 =',self.server_signed_pubkey_ext_b64)
|
||||
self.external_keys_loaded = True
|
||||
|
||||
def keyserver_name_exists(self):
|
||||
user_missing = (self.pubkey_ext_b64 is b'' or self.signed_pubkey_ext_b64 is b'' or self.server_signed_pubkey_ext_b64 is b'')
|
||||
return not user_missing
|
||||
|
||||
|
||||
def meet(self,save_loc=True):
|
||||
pubkey_ext = b64decode(self.pubkey_keyserver_verified)
|
||||
self.log('pubkey_ext',pubkey_ext)
|
||||
self.pubkey=pubkey_ext
|
||||
self.log('setting self.pubkey to external value:',self.pubkey)
|
||||
self.log('self.pubkey_64',self.pubkey_b64)
|
||||
self.log('keyserver_verified',self.pubkey_keyserver_verified)
|
||||
|
||||
with open(self.key_path_pub,'wb') as of:
|
||||
of.write(self.pubkey_keyserver_verified)
|
||||
|
||||
return {'success':'Met person as acquaintance'}
|
||||
|
||||
def register(self):
|
||||
# register
|
||||
if self.create_if_missing:
|
||||
self.gen_keys()
|
||||
res = self.set_externally_signed_pubkey()
|
||||
self.log('set_externally_signed_pubkey res =',res)
|
||||
if res is None:
|
||||
return {'error':'Could not set externally signed pubkey'}
|
||||
else:
|
||||
return {'success':'Created new pubkey'}
|
||||
else:
|
||||
return {'error':'No public key externally, but create_if_missing==False'}
|
||||
|
||||
|
||||
# login, meet, or register
|
||||
def boot(self):
|
||||
# keyserver active?
|
||||
keyserver_pubkey = self.keyserver_pubkey = self.load_keyserver_pubkey()
|
||||
if keyserver_pubkey is None:
|
||||
return {'error':'Cannot conntact keyserver'}
|
||||
|
||||
# load external keys
|
||||
self.load_external_keys()
|
||||
|
||||
# user exists...
|
||||
if self.keyserver_name_exists():
|
||||
|
||||
# if I claim to be this person
|
||||
if self.privkey and self.pubkey_decr:
|
||||
attempt = self.login()
|
||||
self.log('login attempt ->',attempt)
|
||||
return attempt
|
||||
|
||||
# otherwise just meet them
|
||||
attempt = self.meet()
|
||||
self.log('meet attempt ->',attempt)
|
||||
return attempt
|
||||
|
||||
# user does not exist
|
||||
attempt = self.register()
|
||||
self.log('register attempt -->',attempt)
|
||||
return attempt
|
||||
|
||||
@property
|
||||
def key_path_pub(self):
|
||||
return os.path.join(KEY_PATH_PUB,'.'+self.name+'.loc')
|
||||
|
||||
@property
|
||||
def key_path_pub_enc(self):
|
||||
return os.path.join(KEY_PATH_PUB,'.'+self.name+'.loc.box')
|
||||
|
||||
|
||||
@property
|
||||
def key_path_priv(self):
|
||||
return os.path.join(KEY_PATH_PRIV,'.'+self.name+'.key')
|
||||
|
||||
@property
|
||||
def name_b64(self):
|
||||
return b64encode(self.name.encode())
|
||||
|
||||
|
||||
@property
|
||||
def privkey_b64(self):
|
||||
return b64encode(self.privkey)
|
||||
|
||||
@property
|
||||
def pubkey_b64(self):
|
||||
return b64encode(self.pubkey) if self.pubkey else b''
|
||||
|
||||
@property
|
||||
def signed_pubkey_b64(self):
|
||||
return self.sign(self.pubkey_b64)
|
||||
# return self.encrypt(self.pubkey_b64, self.pubkey_b64)
|
||||
|
||||
## genearating keys
|
||||
|
||||
# def gen_keys1(self):
|
||||
# self.log('gen_keys()')
|
||||
# keypair = GenerateKeyPair(KEY_PAIR_TYPE.EC)
|
||||
# self.privkey = keypair.export_private_key()
|
||||
# self.pubkey = keypair.export_public_key()
|
||||
|
||||
# self.log(f'priv_key saved to {self.key_path_priv}')
|
||||
# with open(self.key_path_priv, "wb") as private_key_file:
|
||||
# private_key_file.write(self.privkey_b64)
|
||||
|
||||
# with open(self.key_path_pub, "wb") as public_key_file:
|
||||
# # save SIGNED public key
|
||||
# public_key_file.write(self.pubkey_b64)
|
||||
|
||||
# with open(self.key_path_pub_enc,'wb') as signed_public_key_file:
|
||||
# # self.log('encrypted_pubkey_b64 -->',self.encrypted_pubkey_b64)
|
||||
# pubkey_b64 = b64encode(self.pubkey)
|
||||
# self.log('pubkey',self.pubkey)
|
||||
# self.log('pubkey_b64',pubkey_b64)
|
||||
|
||||
# encrypted_pubkey_b64 = self.encrypt(pubkey_b64, pubkey_b64, KOMRADE_PRIV_KEY)
|
||||
# self.log('encrypted_pubkey_b64 -->',encrypted_pubkey_b64)
|
||||
|
||||
# signed_public_key_file.write(encrypted_pubkey_b64)
|
||||
|
||||
def gen_keys(self,passphrase=DEBUG_DEFAULT_PASSPHRASE):
|
||||
"""
|
||||
Generate private/public key pair
|
||||
Secure that with passphrase
|
||||
"""
|
||||
|
||||
## Generate key pair
|
||||
self.log('gen_keys()')
|
||||
keypair = GenerateKeyPair(KEY_PAIR_TYPE.EC)
|
||||
self.privkey = keypair.export_private_key()
|
||||
self.pubkey = keypair.export_public_key()
|
||||
|
||||
## Secure with passphrase
|
||||
if passphrase is None: passphrase=input('Please protect account with passphrase:\n')
|
||||
cell = SCellSeal(passphrase=passphrase)
|
||||
keypair_b = self.privkey + BSEP + self.pubkey
|
||||
|
||||
keypair_b_encr = cell.encrypt(keypair_b)
|
||||
|
||||
|
||||
self.log(f'priv_key saved to {self.key_path_priv}')
|
||||
with open(self.key_path_priv, "wb") as private_key_file:
|
||||
private_key_file.write(self.privkey_b64)
|
||||
|
||||
with open(self.key_path_pub, "wb") as public_key_file:
|
||||
# save SIGNED public key
|
||||
public_key_file.write(self.pubkey_b64)
|
||||
|
||||
with open(self.key_path_pub_enc,'wb') as signed_public_key_file:
|
||||
# self.log('encrypted_pubkey_b64 -->',self.encrypted_pubkey_b64)
|
||||
pubkey_b64 = b64encode(self.pubkey)
|
||||
self.log('pubkey',self.pubkey)
|
||||
self.log('pubkey_b64',pubkey_b64)
|
||||
|
||||
encrypted_pubkey_b64 = self.encrypt(pubkey_b64, pubkey_b64, KOMRADE_PRIV_KEY)
|
||||
self.log('encrypted_pubkey_b64 -->',encrypted_pubkey_b64)
|
||||
|
||||
signed_public_key_file.write(encrypted_pubkey_b64)
|
||||
|
||||
|
||||
## loading keys from disk
|
||||
|
||||
def find_keys_local(self):
|
||||
self.log(f'find_keys_local(path_pub={self.key_path_pub}, path_priv={self.key_path_priv})')
|
||||
|
||||
if os.path.exists(self.key_path_priv):
|
||||
with open(self.key_path_priv) as priv_f:
|
||||
self.privkey=b64decode(priv_f.read())
|
||||
|
||||
# Load from encrypted?
|
||||
if os.path.exists(self.key_path_pub_enc) and self.privkey:
|
||||
with open(self.key_path_pub_enc) as pub_f:
|
||||
self.pubkey_enc=pub_f.read()
|
||||
self.pubkey_decr=self.decrypt(self.pubkey_enc, KOMRADE_PUB_KEY)
|
||||
# self.pubkey=self.pubkey_decr
|
||||
self.log('loaded self.pubkey from enc',self.pubkey)
|
||||
self.can_receive = True
|
||||
self.can_send = True
|
||||
|
||||
# load from nonencrypted pubkey?
|
||||
if os.path.exists(self.key_path_pub):
|
||||
with open(self.key_path_pub) as pub_f:
|
||||
self.pubkey=b64decode(pub_f.read())
|
||||
self.log('loaded self.pubkey from UNenc',self.pubkey)
|
||||
self.can_receive = True
|
||||
|
||||
@property
|
||||
def pubkey_keyserver_verified(self):
|
||||
# test if remote data agrees
|
||||
if not hasattr(self,'_keyserver_verified'):
|
||||
self._keyserver_verified = self.verify(self.server_signed_pubkey_ext_b64, self.keyserver_pubkey_b64)
|
||||
return self._keyserver_verified
|
||||
|
||||
def login(self):
|
||||
# test if local data present
|
||||
if not self.pubkey or not self.pubkey_decr:
|
||||
return {'error':'Public keys not present'}
|
||||
|
||||
# test if local data agrees
|
||||
self.log('self.pubkey',self.pubkey_b64)
|
||||
self.log('self.pubkey_decr',self.pubkey_decr)
|
||||
if self.pubkey_b64 != self.pubkey_decr:
|
||||
return {'error':'Public keys do not match'}
|
||||
|
||||
# test if remote data agrees
|
||||
keyserver_verified = self.pubkey_keyserver_verified
|
||||
if keyserver_verified is None:
|
||||
return {'error':'Keyserver verification failed'}
|
||||
|
||||
# test if pubkey match
|
||||
enc_match = self.pubkey_decr == keyserver_verified
|
||||
if enc_match:
|
||||
return {'success':'Keys matched'}
|
||||
return {'error':'Keys did not match'}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## E/D/S/V
|
||||
|
||||
def encrypt(self,msg_b64,for_pubkey_b64, privkey_b64=None):
|
||||
self.log('encrypt()',msg_b64,for_pubkey_b64,privkey_b64)
|
||||
|
||||
privkey = b64decode(privkey_b64) if privkey_b64 else self.privkey
|
||||
# handle verification failure
|
||||
for_pubkey = b64decode(for_pubkey_b64)
|
||||
encrypted_msg = SMessage(privkey, for_pubkey).wrap(msg_b64)
|
||||
return b64encode(encrypted_msg)
|
||||
|
||||
def decrypt(self,encrypted_msg_b64,from_pubkey_b64, privkey_b64=None):
|
||||
privkey = b64decode(privkey_b64) if privkey_b64 else self.privkey
|
||||
|
||||
# handle verification failure
|
||||
from_pubkey = b64decode(from_pubkey_b64)
|
||||
encrypted_msg = b64decode(encrypted_msg_b64)
|
||||
decrypted_msg = SMessage(privkey, from_pubkey).unwrap(encrypted_msg)
|
||||
return decrypted_msg
|
||||
|
||||
def sign(self,msg_b64, privkey=None):
|
||||
if not privkey: privkey=self.privkey
|
||||
signed_msg = b64encode(ssign(privkey, msg_b64))
|
||||
return signed_msg
|
||||
|
||||
def verify(self,signed_msg_b64,pubkey_b64=None):
|
||||
if pubkey_b64 is None: pubkey_b64=self.pubkey_b64
|
||||
|
||||
self.log('verify() signed_msg_b64 =',signed_msg_b64)
|
||||
self.log('verify() pubkey_b64 =',pubkey_b64)
|
||||
signed_msg = b64decode(signed_msg_b64)
|
||||
public_key = b64decode(pubkey_b64)
|
||||
|
||||
self.log('verify() signed_msg =',signed_msg)
|
||||
self.log('verify() public_key =',public_key)
|
||||
|
||||
try:
|
||||
verified_msg = sverify(public_key, signed_msg)
|
||||
return verified_msg
|
||||
except ThemisError as e:
|
||||
print('!!',e)
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def uri_inbox(self):
|
||||
return P2P_PREFIX_INBOX+self.name.encode()
|
||||
|
||||
@property
|
||||
def uri_outbox(self):
|
||||
return P2P_PREFIX_OUTBOX+self.name.encode()
|
||||
|
||||
@property
|
||||
def app_pubkey_b64(self):
|
||||
return KOMRADE_PUB_KEY
|
||||
|
||||
|
||||
|
||||
|
||||
## POSTING/SENDING MSGS
|
||||
|
||||
async def post(self,encrypted_payload_b64,to_person):
|
||||
# double wrap
|
||||
double_encrypted_payload = self.encrypt(encrypted_payload_b64, to_person.pubkey_b64, KOMRADE_PRIV_KEY)
|
||||
self.log('double_encrypted_payload =',double_encrypted_payload)
|
||||
|
||||
post_id = get_random_binary_id() #get_random_id().encode()
|
||||
node = await self.node
|
||||
|
||||
uri_post = P2P_PREFIX_POST + post_id
|
||||
res = await node.set(uri_post, double_encrypted_payload)
|
||||
self.log('result of post() =',res)
|
||||
|
||||
return uri_post
|
||||
|
||||
async def send(self,msg_b,to,from_person=None):
|
||||
"""
|
||||
1) [Encrypted payload:]
|
||||
1) Timestamp
|
||||
2) Public key of sender
|
||||
3) Public key of recipient
|
||||
4) AES-encrypted Value
|
||||
2) [Decryption tools]
|
||||
1) AES-decryption key
|
||||
2) AES decryption IV value
|
||||
5) Signature of value by author
|
||||
"""
|
||||
|
||||
if type(msg_b)==str: msg_b=msg_b.encode()
|
||||
msg_b64=b64encode(msg_b)
|
||||
|
||||
# encrypt and sign
|
||||
to_person = to
|
||||
if from_person is None: from_person = self
|
||||
encrypted_payload = from_person.encrypt(msg_b64, to_person.pubkey_b64)
|
||||
signed_encrypted_payload = from_person.sign(encrypted_payload)
|
||||
|
||||
# package
|
||||
time_b64 = b64encode(str(time.time()).encode())
|
||||
WDV_b64 = b64encode(BSEP.join([
|
||||
signed_encrypted_payload,
|
||||
from_person.pubkey_b64,
|
||||
from_person.name_b64,
|
||||
time_b64]))
|
||||
self.log('WDV_b64 =',WDV_b64)
|
||||
|
||||
# post
|
||||
post_id = await self.post(WDV_b64, to_person)
|
||||
self.log('post_id <-',post_id)
|
||||
|
||||
# add to inbox
|
||||
res = await to_person.add_to_inbox(post_id)
|
||||
self.log('add_to_inbox <-',res)
|
||||
|
||||
# add to outbox?
|
||||
# pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async def load_inbox(self,decrypt_msg_uri=False,last=None):
|
||||
node = await self.node
|
||||
encrypted_inbox_idstr_b64 = await node.get(self.uri_inbox)
|
||||
self.log('encrypted_inbox_idstr_b64 =',encrypted_inbox_idstr_b64)
|
||||
if encrypted_inbox_idstr_b64 is None: return []
|
||||
|
||||
# inbox_idstr = self.decrypt(encrypted_inbox_idstr_b64, self.app_pubkey_b64)
|
||||
# self.log('decrypted inbox_idstr =',inbox_idstr)
|
||||
# decrypt!
|
||||
|
||||
encrypted_inbox_idstr = b64decode(encrypted_inbox_idstr_b64)
|
||||
self.log('encrypted_inbox_idstr =',encrypted_inbox_idstr)
|
||||
|
||||
inbox_ids = encrypted_inbox_idstr.split(BSEP) if encrypted_inbox_idstr is not None else []
|
||||
self.log('inbox_ids =',inbox_ids)
|
||||
|
||||
if decrypt_msg_uri:
|
||||
inbox_ids = [self.decrypt(enc_msg_id_b64,KOMRADE_PUB_KEY) for enc_msg_id_b64 in inbox_ids]
|
||||
self.log('inbox_ids decrypted =',inbox_ids)
|
||||
|
||||
return inbox_ids[:last]
|
||||
|
||||
async def add_to_inbox(self,msg_uri,inbox_sofar=None):
|
||||
# encrypt msg id so only inbox owner can resolve the pointer
|
||||
self.log('unencrypted msg uri:',msg_uri)
|
||||
encrypted_msg_uri = self.encrypt(msg_uri, self.pubkey_b64, KOMRADE_PRIV_KEY)
|
||||
self.log('encrypted msg uri:',encrypted_msg_uri)
|
||||
|
||||
# get current inbox
|
||||
if inbox_sofar is None: inbox_sofar=await self.load_inbox()
|
||||
self.log('inbox_sofar:',inbox_sofar)
|
||||
|
||||
# add new value
|
||||
new_inbox = inbox_sofar + [encrypted_msg_uri]
|
||||
new_inbox_b = BSEP.join(new_inbox)
|
||||
self.log('new_inbox_b:',new_inbox_b)
|
||||
|
||||
new_inbox_b64 = b64encode(new_inbox_b)
|
||||
|
||||
self.log('new_inbox_b64:',new_inbox_b64)
|
||||
|
||||
# set on net
|
||||
node = await self.node
|
||||
await node.set(self.uri_inbox,new_inbox_b64)
|
||||
|
||||
new_length = len(new_inbox)
|
||||
return {'success':'Inbox length increased to %s' % new_length}
|
||||
#return {'error':'Could not append data'}
|
||||
|
||||
|
||||
async def add_to_outbox(self):
|
||||
"""
|
||||
Do not store on server!
|
||||
"""
|
||||
pass
|
||||
|
||||
async def read_inbox(self,uri_inbox=None):
|
||||
if uri_inbox is None: uri_inbox = P2P_PREFIX_INBOX+self.name.encode()
|
||||
node = await self.node
|
||||
inbox_ids = await node.get(uri_inbox)
|
||||
if inbox_ids is not None:
|
||||
inbox_ids = inbox_ids.split(BSEP)
|
||||
self.log('found inbox IDs:',inbox_ids)
|
||||
|
||||
msgs_toread = [self.read_msg(msg_id) for msg_id in inbox_ids]
|
||||
msgs = await asyncio.gather(*msgs_toread)
|
||||
self.log('read_inbox() msgs = ',msgs)
|
||||
return msgs
|
||||
return []
|
||||
|
||||
async def read_outbox(self,uri_outbox=None):
|
||||
if uri_outbox is None: uri_outbox = P2P_PREFIX_OUTBOX+self.name.encode()
|
||||
return await self.read_inbox(uri_outbox)
|
||||
|
||||
|
||||
async def read_msg(self,msg_id):
|
||||
self.log(f'Persona.read_msg({msg_id}) ?')
|
||||
uri_msg=P2P_PREFIX_POST+msg_id
|
||||
node = await self.node
|
||||
|
||||
res = await node.get(uri_msg)
|
||||
self.log('res = ',res)
|
||||
if res is not None:
|
||||
double_encrypted_payload_b64 = res
|
||||
single_encrypted_payload = self.decrypt(double_encrypted_payload_b64, KOMRADE_PUB_KEY)
|
||||
self.log('GOT ENRYPTED PAYLOAD:',single_encrypted_payload)
|
||||
|
||||
signed_encrypted_payload_b64,from_pubkey_b64,name_b64,time_b64 = single_encrypted_payload.split(BSEP)
|
||||
self.log('signed_encrypted_payload =',signed_encrypted_payload_b64)
|
||||
self.log('from_pubkey_b64 =',from_pubkey_b64)
|
||||
self.log('time_b64 =',time_b64)
|
||||
|
||||
from_name = b64decode(name_b64).decode()
|
||||
self.log('from_name =',from_name)
|
||||
timestamp = b64decode(time_b64).decode()
|
||||
tmpP = Komrade(from_name)
|
||||
await tmpP.boot()
|
||||
from_pubkey_b64_accto_name = tmpP.pubkey_b64
|
||||
assert from_pubkey_b64==from_pubkey_b64_accto_name
|
||||
|
||||
encrypted_payload_b64 = self.verify(signed_encrypted_payload_b64, from_pubkey_b64)
|
||||
self.log('encrypted_payload_b64 =',encrypted_payload_b64)
|
||||
|
||||
payload = self.decrypt(encrypted_payload_b64, from_pubkey_b64)
|
||||
self.log('payload =',payload)
|
||||
return {
|
||||
'success':True,
|
||||
'content':payload,
|
||||
'from_name':from_name,
|
||||
'from_pubkey_b64':from_pubkey_b64,
|
||||
'timestamp':timestamp
|
||||
}
|
||||
return {'error':'Unknown'}
|
||||
|
||||
|
||||
|
||||
def run_multiple_tasks(tasks):
|
||||
async def _go(tasks):
|
||||
res = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
return res
|
||||
return asyncio.get_event_loop().run_until_complete(_go(tasks))
|
||||
|
||||
async def main():
|
||||
|
||||
# start node
|
||||
from kademlia.network import Server
|
||||
#from p2p_api import
|
||||
PORT_LISTEN = 5969
|
||||
|
||||
# NODES_PRIME = [("128.232.229.63",8467), ("68.66.241.111",8467)]
|
||||
NODES_PRIME = [("128.232.229.63",8467)]
|
||||
|
||||
node = Server(log=log)
|
||||
await node.listen(PORT_LISTEN)
|
||||
await node.bootstrap(NODES_PRIME)
|
||||
|
||||
marx = Komrade('marx',node=node)
|
||||
elon = Komrade('elon2',node=node)
|
||||
world = Komrade('world',node=node)
|
||||
await world.boot()
|
||||
|
||||
|
||||
await marx.boot()
|
||||
await elon.boot()
|
||||
|
||||
# await marx.send(b'Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! Secret message 2! ',to=elon)
|
||||
|
||||
|
||||
# await elon.read_inbox()
|
||||
|
||||
await marx.send(b'A specter is haunting the internet',to=world)
|
||||
await elon.send(b'My rockets explode and so will your mind',to=world)
|
||||
await elon.send(b'My rockets explode and so will your mind',to=world)
|
||||
|
||||
await world.read_inbox()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
python -c "import api; api.boot_lonely_selfless_node()"
|
@ -12,7 +12,7 @@ from komrade.backend.keymaker import *
|
||||
|
||||
class KomradeX(Caller):
|
||||
|
||||
def __init__(self, name=None, passphrase=DEBUG_DEFAULT_PASSPHRASE):
|
||||
def __init__(self, name=None, pubkey=None, passphrase=DEBUG_DEFAULT_PASSPHRASE):
|
||||
super().__init__(name=name,passphrase=passphrase)
|
||||
# self.log(f'booted komrade with {name} and {passphrase} and\n\n{dict_format(self.keychain())}')
|
||||
# if SHOW_STATUS:
|
||||
@ -27,6 +27,8 @@ class KomradeX(Caller):
|
||||
# yes? -- login
|
||||
|
||||
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
|
||||
@ -203,29 +205,30 @@ class KomradeX(Caller):
|
||||
)
|
||||
|
||||
def login(self,passphrase=None):
|
||||
# check hardware
|
||||
if not self.pubkey:
|
||||
self.log('''Login impossible. I do not have this komrade's public key, much less private one.''')
|
||||
return
|
||||
if not self.privkey_encr:
|
||||
self.log('''Login impossible. I do not have this komrade's private key on this hardware.''')
|
||||
return
|
||||
# what keys do I have?
|
||||
keys = self.keychain()
|
||||
# self.log('here are my keys:',dict_format(keys))
|
||||
|
||||
# check password
|
||||
while not passphrase:
|
||||
from getpass import getpass
|
||||
passphrase = getpass('@Keymaker: Enter password for {self} in order to decrypt the encrypted private key:\n\n')
|
||||
|
||||
# assemble privkey?
|
||||
privkey = self.keychain(passphrase=passphrase).get('privkey')
|
||||
if not privkey:
|
||||
self.log('''Login impossible. I do not have this komrade's private key on this hardware.''')
|
||||
return
|
||||
|
||||
# check hardware
|
||||
if not 'pubkey' in keys:
|
||||
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}
|
||||
|
||||
if not 'privkey' in keys:
|
||||
emsg='''Login failed. Private key did not unlock from passphrase.'''
|
||||
return {'success':False, 'status': emsg}
|
||||
|
||||
# compose message
|
||||
msg = {
|
||||
'name':self.name,
|
||||
'pubkey':self.pubkey.data,
|
||||
'pubkey':keys['pubkey'].data,
|
||||
'secret_login':self.secret_login
|
||||
}
|
||||
|
||||
@ -270,18 +273,41 @@ class KomradeX(Caller):
|
||||
|
||||
|
||||
|
||||
def send_msg_to(self,msg,to_whom):
|
||||
to_whom = self.find(to_whom)
|
||||
self.log(f'found {to_whom}')
|
||||
def msg(self,someone,something):
|
||||
# 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}!''')
|
||||
|
||||
msg_obj = self.compose_msg_to(
|
||||
msg,
|
||||
to_whom
|
||||
something,
|
||||
someone
|
||||
)
|
||||
self.log('composed msg:',msg_obj)
|
||||
msg_obj.encrypt()
|
||||
self.log('going to send msg_d?',msg_obj.msg_d)
|
||||
|
||||
return self.ring_ring(msg_obj.msg_d)
|
||||
# encrypting
|
||||
msg_obj.encrypt()
|
||||
|
||||
# attaching
|
||||
direct_msg_data = msg_obj.msg
|
||||
self.log('going to send only this to op:',direct_msg_data)
|
||||
|
||||
# enclosing
|
||||
msg_to_op = {
|
||||
'deliver_from':self.pubkey.data_b64,
|
||||
'deliver_to':someone.pubkey.data_b64,
|
||||
'deliver_msg':direct_msg_data,
|
||||
}
|
||||
|
||||
self.log('going to send msg to op?',msg_to_op)
|
||||
|
||||
return self.ring_ring(
|
||||
msg_to_op,
|
||||
route='deliver_msg'
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -304,17 +330,20 @@ def test_msg():
|
||||
z.send_msg_to('you ssssssuck')
|
||||
|
||||
|
||||
def test_loading():
|
||||
# z1 = Komrade('zuck')
|
||||
# print(z1.keychain())
|
||||
|
||||
z2 = Komrade(b'VUVDMgAAAC08BCMVA+0dMJXc66/W7hty669+3/3S61Q1yjmgJW8I0k3lqfDi')
|
||||
print(z2)
|
||||
print(z2.keychain())
|
||||
|
||||
pprint(PHONEBOOK)
|
||||
|
||||
return
|
||||
|
||||
# z1.login()
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
test_msg()
|
||||
# marx = Komrade('marx')
|
||||
# elon = Komrade('elon')
|
||||
|
||||
# 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'})
|
||||
|
||||
# # print(marx.exists_on_server())
|
||||
test_loading()
|
@ -8,7 +8,13 @@ from komrade.backend import *
|
||||
PHONEBOOK = {}
|
||||
|
||||
# Factory constructor
|
||||
def Komrade(name,pubkey=None,*x,**y):
|
||||
def Komrade(name=None,pubkey=None,*x,**y):
|
||||
if issubclass(type(name),Operator): return name
|
||||
if name and not pubkey and type(name)==bytes:
|
||||
pubkey=b64enc(name)
|
||||
name=None
|
||||
|
||||
|
||||
from komrade.backend.the_operator import TheOperator
|
||||
from komrade.backend.the_telephone import TheTelephone
|
||||
from komrade.backend.komrades import KomradeX
|
||||
@ -28,6 +34,7 @@ def Komrade(name,pubkey=None,*x,**y):
|
||||
if name==TELEPHONE_NAME:
|
||||
kommie = TheTelephone() #(*x,**y)
|
||||
else:
|
||||
print('booting new kommie')
|
||||
kommie = KomradeX(name,*x,**y)
|
||||
|
||||
# print('found!',name,PHONEBOOK[name],PHONEBOOK[name].keychain())
|
||||
@ -44,26 +51,31 @@ from komrade.constants import OPERATOR_ROUTES
|
||||
class Operator(Keymaker):
|
||||
ROUTES = OPERATOR_ROUTES
|
||||
|
||||
def __eq__(self,other):
|
||||
return self.pubkey.data == other.pubkey.data
|
||||
|
||||
def __init__(self, name=None, passphrase=DEBUG_DEFAULT_PASSPHRASE, pubkey=None, keychain = {}, path_crypt_keys=PATH_CRYPT_CA_KEYS, path_crypt_data=PATH_CRYPT_CA_DATA):
|
||||
global PHONEBOOK
|
||||
# print('booting opertor with ...',name,pubkey,'??')
|
||||
|
||||
if pubkey:
|
||||
# print(pubkey,'pubkey !?')
|
||||
assert type(pubkey)==bytes
|
||||
pubkey = b64dec(pubkey)
|
||||
if keychain.get('pubkey'):
|
||||
kcpubk=keychain.get('pubkey').data if type(keychain.get('pubkey'))!=bytes else keychain.get('pubkey')
|
||||
else:
|
||||
keychain['pubkey']=pubkey #KomradeAsymmetricPublicKey(pubkey)
|
||||
def __init__(self,
|
||||
name=None,
|
||||
pubkey=None,
|
||||
keychain = {},
|
||||
path_crypt_keys=PATH_CRYPT_CA_KEYS,
|
||||
path_crypt_data=PATH_CRYPT_CA_DATA
|
||||
):
|
||||
|
||||
super().__init__(name=name,passphrase=passphrase, keychain=keychain,
|
||||
path_crypt_keys=path_crypt_keys, path_crypt_data=path_crypt_data)
|
||||
global PHONEBOOK
|
||||
|
||||
# call Keymaker's intro
|
||||
super().__init__(
|
||||
name=name,
|
||||
keychain=keychain,
|
||||
path_crypt_keys=path_crypt_keys,
|
||||
path_crypt_data=path_crypt_data
|
||||
)
|
||||
|
||||
|
||||
# add to phonebook
|
||||
PHONEBOOK[name]=self
|
||||
if name: PHONEBOOK[name]=self
|
||||
if self.pubkey: PHONEBOOK[self.pubkey.data_b64]
|
||||
|
||||
|
||||
@property
|
||||
@ -95,7 +107,7 @@ class Operator(Keymaker):
|
||||
return OPERATOR
|
||||
|
||||
|
||||
def compose_msg_to(self,msg,another,incl_from_name=1,incl_to_name=1):
|
||||
def compose_msg_to(self,msg,another,incl_from_name=True,incl_to_name=True):
|
||||
if not self.privkey or not self.pubkey:
|
||||
raise KomradeException('why do I have no pub/privkey pair!?',self,self.name,self.pubkey,self.privkey,self.keychain())
|
||||
if not another.name or not another.pubkey:
|
||||
@ -164,7 +176,8 @@ class Operator(Keymaker):
|
||||
# route it!
|
||||
self.log(f'Routing msg to {self}.{route}():\n\n{dict_format(msg_obj.data,tab=4)}')
|
||||
func = getattr(self,route)
|
||||
new_data = func(**data)
|
||||
# new_data = func(**data)
|
||||
new_data = func(msg_obj)
|
||||
self.log(f'New data was received back from {self}.{route}() route:\b\b{dict_format(new_data,tab=4)}')
|
||||
msg_obj.msg = msg_obj.msg_d['msg'] = new_data
|
||||
|
||||
|
@ -109,20 +109,6 @@ class TheOperator(Operator):
|
||||
# return back to phone and back down to chain
|
||||
return msg_sealed
|
||||
|
||||
|
||||
|
||||
|
||||
def send(self,encr_data_b):
|
||||
self.log(type(encr_data_b),encr_data_b,'sending!')
|
||||
return encr_data_b
|
||||
|
||||
### ROUTES
|
||||
|
||||
def does_username_exist(self,name,**data):
|
||||
pubkey=self.crypt_keys.get(name,prefix='/pubkey/')
|
||||
self.log(f'looking for {name}, found {pubkey} as pubkey')
|
||||
return bool(pubkey)
|
||||
|
||||
def has_user(self,name=None,pubkey=None):
|
||||
nm,pk = name,pubkey
|
||||
if pubkey: pk=self.crypt_keys.get(
|
||||
@ -138,7 +124,28 @@ class TheOperator(Operator):
|
||||
# self.log('nms:',name,nm)
|
||||
return (pubkey and pk) or (name and nm)
|
||||
|
||||
def login(self,name,pubkey,secret_login,**data):
|
||||
|
||||
|
||||
def send(self,encr_data_b):
|
||||
self.log(type(encr_data_b),encr_data_b,'sending!')
|
||||
return encr_data_b
|
||||
|
||||
### ROUTES
|
||||
|
||||
def does_username_exist(self,msg_obj):
|
||||
data=msg_obj.data
|
||||
name=data.get('name')
|
||||
pubkey=self.crypt_keys.get(name,prefix='/pubkey/')
|
||||
self.log(f'looking for {name}, found {pubkey} as pubkey')
|
||||
return bool(pubkey)
|
||||
|
||||
|
||||
def login(self,msg_obj):
|
||||
data=msg_obj.data
|
||||
name=data.get('name')
|
||||
pubkey=data.get('pubkey')
|
||||
secret_login=data.get('secret_login')
|
||||
|
||||
name=name.encode() if type(name)==str else name
|
||||
pubkey=pubkey.encode() if type(pubkey)==str else pubkey
|
||||
secret_login=secret_login.encode() if type(secret_login)==str else secret_login
|
||||
@ -200,8 +207,11 @@ class TheOperator(Operator):
|
||||
'status':'Login failed.'
|
||||
}
|
||||
|
||||
def register_new_user(self,name,pubkey,**data):
|
||||
def register_new_user(self,msg_obj):
|
||||
# self.log('setting pubkey under name')
|
||||
data=msg_obj.data
|
||||
name=data.get('name')
|
||||
pubkey=data.get('pubkey')
|
||||
|
||||
# is user already there?
|
||||
if self.has_user(name=name,pubkey=pubkey):
|
||||
@ -269,8 +279,19 @@ class TheOperator(Operator):
|
||||
|
||||
# self.log('Operator returning result:',dict_format(res,tab=2))
|
||||
|
||||
def deliver_msg(self,msg,to_whom,**data):
|
||||
pass
|
||||
def deliver_msg(self,msg_to_op):
|
||||
data = msg_to_op.data
|
||||
deliver_to = data.get('deliver_to')
|
||||
deliver_from = data.get('deliver_from')
|
||||
deliver_msg = data.get('deliver_msg')
|
||||
|
||||
if not deliver_to or not deliver_from or not deliver_msg:
|
||||
return {'success':'False', 'status':'Invalid input.'}
|
||||
|
||||
to_komrade = Komrade(pubkey=deliver_to)
|
||||
from_komrade = Komrade(pubkey=deliver_from)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -85,7 +85,7 @@ class CLI(Logger):
|
||||
|
||||
|
||||
def login(self,name):
|
||||
print(self,name,self.name,self.komrade,self.loggedin)
|
||||
# print(self,name,self.name,self.komrade,self.loggedin)
|
||||
if not name: name=input('name: ')
|
||||
if not name: return
|
||||
self.komrade=Komrade(name)
|
||||
@ -99,7 +99,7 @@ class CLI(Logger):
|
||||
self.name=None
|
||||
self.loggedin=False
|
||||
self.komrade=None
|
||||
if 'status' in res:
|
||||
if res and 'status' in res:
|
||||
print('@Operator: '+res.get('status','?')+'\n')
|
||||
|
||||
@property
|
||||
|
@ -157,8 +157,8 @@ DEFAULT_USER_SETTINGS = {
|
||||
|
||||
SHOW_LOG = 1
|
||||
SHOW_STATUS = 0
|
||||
PAUSE_LOGGER = 0
|
||||
CLEAR_LOGGER = 0
|
||||
PAUSE_LOGGER = 1
|
||||
CLEAR_LOGGER = 1
|
||||
|
||||
CLI_TITLE = 'KOMRADE'
|
||||
CLI_FONT = 'clr5x6'#'colossal'
|
||||
|
Loading…
Reference in New Issue
Block a user