You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Comrad/komrade/backend/the_operator.py

221 lines
9.1 KiB
Python

4 years ago
"""
There is only one operator!
Running on node prime.
"""
# internal 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 import *
4 years ago
from komrade.backend import *
4 years ago
# print(PATH_OPERATOR_WEB_KEYS_URL)
4 years ago
class TheOperator(Operator):
"""
4 years ago
The remote operator
4 years ago
"""
4 years ago
@property
def phone(self):
4 years ago
global TELEPHONE
4 years ago
from komrade.backend.the_telephone import TheTelephone
4 years ago
if not TELEPHONE: TELEPHONE=TheTelephone()
4 years ago
return TELEPHONE
4 years ago
4 years ago
def __init__(self, name = OPERATOR_NAME, passphrase='acc'):
4 years ago
"""
Boot up the operator. Requires knowing or setting a password of memory.
"""
4 years ago
super().__init__(
name,
passphrase,
path_crypt_keys=PATH_CRYPT_OP_KEYS,
4 years ago
path_crypt_data=PATH_CRYPT_OP_DATA
)
4 years ago
self._keychain = self.load_keychain_from_bytes(self.operator_keychain)
4 years ago
4 years ago
def ring(self,
4 years ago
from_caller=None,
to_caller=None,
json_phone2phone={},
json_caller2phone={}, # (person) -> operator or operator -> (person)
json_caller2caller={}):
4 years ago
encr_msg_to_send = super().ring(
4 years ago
from_phone=self,
to_phone=self.phone,
from_caller=from_caller,
to_caller=to_caller,
json_phone2phone=json_phone2phone,
json_caller2phone=json_caller2phone, # (person) -> operator
json_caller2caller=json_caller2caller)
return self.send(encr_msg_to_send)
4 years ago
# ends the ring_ring() chain
4 years ago
def answer_phone(self,data_b):
4 years ago
# route incoming call from the switchboard
4 years ago
self.log('Hello, this is the Operator. You said: ',data_b)
4 years ago
# unseal
4 years ago
msg_obj = self.unseal_msg(
data_b,
from_whom=self.phone
)
4 years ago
self.log(f'Operator understood message: {msg_obj} {msg_obj.route}')
4 years ago
4 years ago
# decrypt?
msg_obj.decrypt()
4 years ago
# carry out message instructions
4 years ago
resp_msg_obj = self.pronto_pronto(msg_obj) #,route=msg_obj.route)
4 years ago
self.log('route_result <-',resp_msg_obj)
4 years ago
# send back down encrypted
4 years ago
msg_sealed = self.seal_msg(resp_msg_obj.msg_d)
4 years ago
# return back to phone and back down to chain
return msg_sealed
4 years ago
4 years ago
def find_pubkey(self):
return self.operator_keychain['pubkey']
4 years ago
4 years ago
4 years ago
def send(self,encr_data_b):
4 years ago
self.log(type(encr_data_b),encr_data_b,'sending!')
4 years ago
return encr_data_b
4 years ago
### ROUTES
4 years ago
def does_username_exist(self,name,**data):
4 years ago
pubkey=self.crypt_keys.get(name,prefix='/pubkey/')
4 years ago
self.log(f'looking for {name}, found {pubkey} as pubkey')
return bool(pubkey)
4 years ago
4 years ago
def register_new_user(self,name,passphrase,pubkey,**data):
4 years ago
# self.log('setting pubkey under name')
4 years ago
success,ck,cv_b64 = self.crypt_keys.set(name,pubkey,prefix='/pubkey/')
4 years ago
self.log(f'''
got result from crypt:
success = {success}
ck = {ck}
4 years ago
cv = {cv_b64}
4 years ago
''')
4 years ago
# check input back from crypt
4 years ago
# if success and b64decode(cv)!=pubkey: success=False
# if success and name!=self.crypt_keys.key2hash(name): success=False
4 years ago
res = {
'success':success,
4 years ago
'pubkey':b64decode(cv_b64),
4 years ago
'name':name,
}
4 years ago
if not success:
res['status']=self.status(f"{OPERATOR_INTRO}I'm sorry, but I can't register the name of {name}.")
self.log('Operator returning result:',dict_format(res,tab=2))
return res
4 years ago
4 years ago
# generate these admin keys?
admin_keys = self.forge_new_keys(
name=name,
passphrase=passphrase,
keys_to_gen = [
'adminkey',
'adminkey_encr',
'adminkey_decr'
],
4 years ago
keys_to_save=[],
4 years ago
keys_to_return = [
4 years ago
'adminkey',
'adminkey_encr',
'adminkey_decr',
4 years ago
],
key_types = {
'adminkey':KomradeSymmetricKeyWithoutPassphrase(),
4 years ago
'adminkey_encr':KomradeEncryptedKey(),
4 years ago
'adminkey_decr':KomradeSymmetricKeyWithPassphrase(passphrase=passphrase)
4 years ago
}
)
self.log('generated admin keys:',admin_keys)
if not admin_keys:
res['res']=False
res['status']=self.status(f"{OPERATOR_INTRO}I'm sorry, but I couldn't register {name} right now.")
return res
4 years ago
# get settings
settings = DEFAULT_USER_SETTINGS
settings_b = pickle.dumps(settings)
# use admin key to encrypt
adminkey=admin_keys['adminkey']
settings_b_encr = adminkey.encrypt(settings_b)
# set in crypt
key_to_be_hashed = cv_b64 + passphrase
4 years ago
self.crypt_keys.set(key_to_be_hashed, settings_b_encr, prefix='/settings/')
self.crypt_keys.set(key_to_be_hashed, settings_b_encr, prefix='/adminkey_encr/')
4 years ago
# give back decryptor
4 years ago
4 years ago
## success msg
4 years ago
#
4 years ago
cvb64=cv_b64#b64encode(cv).decode()
4 years ago
qrstr=self.qr_str(cvb64)
res['status']=self.status(f'''{OPERATOR_INTRO}I have successfully registered Komrade {name}.
If you're interested, here's what I did. I stored the public key you gave me, {cvb64}, under the name of "{name}". However, I never save that name directly, but record it only in a disguised, "hashed" form: {ck}. I scrambled "{name}" by running it through a 1-way hashing function, which will always yield the same result: provided you know which function I'm using, and what the secret "salt" is that I add to all the input, a string of text which I keep protected and encrypted on my local hard drive.
4 years ago
4 years ago
The content of your data will therefore not only be encrypted, but its location in my database is obscured even to me. There's no way for me to reverse-engineer the name of {name} from the record I stored it under, {ck}. Unless you explictly ask me for the public key of {name}, I will have no way of accessing that information.
Your name ({name}) and your public key ({cvb64}) are the first two pieces of information you've given me about yourself. Your public key is your 'address' in Komrade: in order for anyone to write to you, or for them to receive messages from you, they'll need to know your public key (and vise versa). The Komrade app should store your public key on your device as a QR code, under ~/.komrade/.contacts/{name}.png. It will look something like this:{qrstr}You can then send this image to anyone by a secure channel (Signal, IRL, etc), or tell them the code directly ({cvb64}).
By default, if anyone asks me what your public key is, I won't tell them--though I won't be able to avoid hinting that a user exists under this name should someone try to register under that name and I deny them). Instead, if the person who requested your public key insists, I will send you a message (encrypted end-to-end so only you can read it) that the user who met someone would like to introduce themselves to you; I will then send you their name and public key. It's now your move: up to you whether to save them back your public key.
If you'd like to change this default behavior, e.g. by instead allowing anyone to request your public key, except for those whom you explcitly block, I have also created a super secret administrative record for you to change various settings on your account. This is protected by a separate encryption key which I have generated for you; and this key which is itself encrypted with the password you entered earlier. Don't worry: I never saw that password you typed, since it was given to me already hashed and disguised (as something {len(passphrase)} characters long, ending in "{passphrase[:10]}"). Without that hashed passphrase, no one will be able to unlock the administration key; and without the administration key, they won't be able to find the hashed record I stored your user settings under, since I also salted that hash with your own hashed passphrase. Even if someone found the record I stored them under, they wouldn't be able to decrypt the existing settings; and if they can't do that, I won't let them overwrite the record.''')
self.log('Operator returning result:',dict_format(res,tab=2))
4 years ago
4 years ago
def test_op():
4 years ago
from komrade.backend.the_telephone import TheTelephone
op = TheOperator()
# op.boot()
4 years ago
keychain_op = op.keychain(force=True)
4 years ago
4 years ago
4 years ago
phone = TheTelephone()
# phone.boot()
4 years ago
keychain_ph = phone.keychain(force=True)
4 years ago
from pprint import pprint
4 years ago
print('REASSEMBLED OPERATOR KEYCHAIN')
pprint(keychain_op)
# stop
4 years ago
print('REASSEMBLED TELEPHONE KEYCHAIN')
4 years ago
pprint(keychain_ph)
4 years ago
4 years ago
# print(op.pubkey(keychain=keychain))
4 years ago
# print(op.crypt_keys.get(op.pubkey(), prefix='/privkey_encr/'))
4 years ago
# print(op.crypt_keys.get(op.name, prefix='/pubkey_encr/'))
4 years ago
# print(op.pubkey_)
4 years ago
4 years ago
4 years ago
# stop
4 years ago
4 years ago
# pubkey = op.keychain()['pubkey']
# pubkey_b64 = b64encode(pubkey)
# print(pubkey)
4 years ago
if __name__ == '__main__': test_op()