diff --git a/komrade/__init__.py b/komrade/__init__.py index d241cd8..1b5d28c 100644 --- a/komrade/__init__.py +++ b/komrade/__init__.py @@ -6,6 +6,7 @@ from .utils import * import os,sys from collections import defaultdict from base64 import b64encode,b64decode +import ujson as json # common external imports from pythemis.skeygen import KEY_PAIR_TYPE, GenerateKeyPair @@ -14,3 +15,4 @@ from pythemis.skeygen import GenerateSymmetricKey from pythemis.scell import SCellSeal from pythemis.exception import ThemisError import getpass + diff --git a/komrade/constants.py b/komrade/constants.py index 3bc2068..3e94fda 100644 --- a/komrade/constants.py +++ b/komrade/constants.py @@ -1,6 +1,6 @@ # addresses URL_KOMRADE = '128.232.229.63' #'komrade.app' -OPERATOR_API_URL = f'http://{URL_KOMRADE}:6999/op/' +OPERATOR_API_URL = f'http://{URL_KOMRADE}:6999/op/req/' # paths @@ -16,10 +16,9 @@ PATH_CRYPT_DATA = os.path.join(PATH_OPERATOR,'.op.db.data.encr') BSEP=b'||||||||||' BSEP2=b'@@@@@@@@@@' BSEP3=b'##########' - -OPERATOR_PUBKEY_b64 = b'VUVDMgAAAC2uQwUQAoxUODIy1nGUKc3gnDe94XxFtsMOJMZ8MN9QMrl3nPiP' -import base64 -OPERATOR_PUBKEY = base64.b64decode(OPERATOR_PUBKEY_b64) +OPERATOR_PUBKEY = b'UEC2\x00\x00\x00-\x80\x99^\xef\x03PN\xc93`k\xa0\\A\xf6\\q\x0c\x8b\xa6\x1c\xc7I\xfbC\x96\xb5w\xf9\x83U\xe7\x0b\x8fp)\xaa' +TELEPHONE_PUBKEY = b'UEC2\x00\x00\x00-^_\x9a\xad\x02\x1b8\x1b2\xa0U\xb8\xf1\x9ek\x00\x9fO\xb7\xa3\xf3\x1a\x9c\xe0/\xeb\xe9\xfe\xe9\xb8\x14\x9dE\x83\x07V\xe4' +TELEPHONE_PRIVKEY = b'REC2\x00\x00\x00-?Y\xcb\xed\x00+4\xe5M\x8fC\xdd\xb6\xe3\xe4Z)\x01\xbc\x02\r\xb8X\xa4\\\x7f\xb2\xa8\xaeu\xdd\xa6\x84\xe1E\xde\x08' # key names diff --git a/komrade/operators/keymaker.py b/komrade/operators/keymaker.py index 7f64238..51289e8 100644 --- a/komrade/operators/keymaker.py +++ b/komrade/operators/keymaker.py @@ -163,7 +163,7 @@ class Keymaker(Logger): # Concrete keys @property - def pubkey__(self): return self.keychain()['pubkey'] + def pubkey_(self): return self.keychain()['pubkey'] @property def privkey_(self, **kwargs): return self.keychain()['privkey'] @property diff --git a/komrade/operators/operators.py b/komrade/operators/operators.py index 170ad73..31dc505 100644 --- a/komrade/operators/operators.py +++ b/komrade/operators/operators.py @@ -14,6 +14,8 @@ from flask import Flask, request, jsonify from flask_classful import FlaskView +OPERATOR_NAME = 'TheOperator' +TELEPHONE_NAME = 'TheTelephone' class Operator(Keymaker): @@ -39,13 +41,13 @@ class Caller(Operator): """ @property - def op(self): + def phone(self): """ Operator on the line. """ - if not hasattr(self,'_op'): - self._op = TheOperatorOnThePhone(caller = self) - return self._op + if not hasattr(self,'_phone'): + self._phone = TheTelephone(caller = self) + return self._phone def get_new_keys(self,pubkey_pass = None, privkey_pass = None, adminkey_pass = None): """ @@ -54,7 +56,7 @@ class Caller(Operator): """ # Get decryptor keys back from The Operator (one half of the Keymaker) - keychain = self.op.forge_new_keys(self.name) + keychain = self.forge_new_keys(self.name) self.log('create_keys() res from Operator? <-',keychain) # Now lock the decryptor keys away, sealing it with a password of memory! @@ -66,38 +68,25 @@ class TheOperator(Operator): """ - def __init__(self, name = 'TheOperator', passphrase=None): + def __init__(self, name = OPERATOR_NAME, passphrase='acc'): """ Boot up the operator. Requires knowing or setting a password of memory. """ # init req paths if not os.path.exists(PATH_OPERATOR): os.makedirs(PATH_OPERATOR) - - if not passphrase: passphrase=getpass.getpass('Hello, this is the Operator speaking. What is the passphrase?\n> ') super().__init__(name,passphrase) - ## boot up if necessary - # Do I have my keys? - have_keys = self.exists() - self.log('I have my keys?',have_keys) - # If not, forge them -- only once! - if not have_keys: - self.get_new_keys() - - # load keychain into memory - self._keychain = self.keychain(force = True) - ### ACTUAL PHONE CONNECTIONS -class TheOperatorOnThePhone(object): +class TheTelephone(Logger): """ API client class for Caller to interact with The Operator. """ def __init__(self, caller): self.caller = caller - + @property def sess(self): """ @@ -107,15 +96,39 @@ class TheOperatorOnThePhone(object): self._sess = get_tor_proxy_session() return self._sess - def req(self,req_json={},req_data=None): - req_json_s = jsonify(req_json) - req_json_s.encode() + def req(self,json_coming_from_phone={},json_coming_from_caller={}): + # Two parts of every request: - # encrypt - from_privkey = self.caller.privkey_ - to_pubkey = OPERATOR_PUBKEY - encrypted_msg = SMessage(from_privkey, for_pubkey).wrap(msg_b64) - return b64encode(encrypted_msg) + # 1) only overall encryption layer E2EE Telephone -> Operator: + + req_data = [] + if json_coming_from_phone: + json_coming_from_phone_s = json.dumps(json_coming_from_phone) + json_coming_from_phone_b = json_coming_from_phone_s.encode() + #json_coming_from_phone_b_encr = SMessage(TELEPHONE_PRIVKEY,OPERATOR_PUBKEY).wrap(json_coming_from_phone_b) + else: + json_coming_from_phone_b=b'' + + # 2) (optional) extra E2EE encrypted layer Caller -> Operator + if json_coming_from_caller: + json_coming_from_caller_s = json.dumps(json_coming_from_caller) + json_coming_from_caller_b = json_coming_from_caller_s.encode() + json_coming_from_caller_b_encr = SMessage(self.caller.privkey_,OPERATOR_PUBKEY).wrap(json_coming_from_caller_b) + else: + json_coming_from_caller_b_encr = b'' + + # encrypt whole package E2EE, Telephone to Operator + req_data = json_coming_from_phone_b + BSEP + json_coming_from_caller_b_encr + req_data_encr = SMessage(TELEPHONE_PRIVKEY, OPERATOR_PUBKEY).wrap(req_data) + req_data_encr_b64 = b64encode(req_data_encr) + self.log('req_data_encr_b64 <--',req_data_encr_b64) + + # send! + res = self.sess.post(OPERATOR_API_URL, data=req_data_encr_b64) + self.log('result from operator?',res) + + return res + def forge_new_keys(self, name, pubkey_is_public=False): req_json = {'name':name, 'pubkey_is_public':pubkey_is_public} @@ -125,28 +138,45 @@ class TheOperatorOnThePhone(object): OPERATOR = None -class TheSwitchboard(FlaskView): +class TheSwitchboard(FlaskView, Logger): default_methods = ['POST'] - def forge_new_keys(self): - content = request.json + def req(self): + data = request.data + self.log('incoming_data! <--',data) - #return f'{name}\n{pubkey_is_public}\n{return_all_keys}' - - def something(self): - return 'something' + # step 1: decode + data = b64decode(data) + self.log('decoded data:',data) + # step 2: decrypt from phone + data = SMessage(OPERATOR.privkey_, TELEPHONE_PUBKEY).unwrap(data) + self.log('decrypted data:',data) -def run_forever(): - + return data +def run_forever(): global OPERATOR OPERATOR = TheOperator() app = Flask(__name__) TheSwitchboard.register(app, route_base='/op/', route_prefix=None) - app.run(debug=True, port=6999) + app.run(debug=True, port=6999, host='0.0.0.0') +def init_operators(): + op = Operator(name=OPERATOR_NAME) + phone = Operator(name=TELEPHONE_NAME) + op.get_new_keys() + phone.get_new_keys() + + op_pub = op.pubkey_ + phone_pub = phone.pubkey_ + phone_priv = phone.privkey_ + + print('OPERATOR_PUBKEY',op_pub) + print('TELEPHONE_PUBKEY =',phone_pub) + print('TELEPHONE_PRIVKEY =',phone_priv) + def test_op(): op = TheOperator() @@ -160,9 +190,18 @@ def test_op(): pubkey = op.keychain()['pubkey'] pubkey_b64 = b64encode(pubkey) - print(pubkey_b64) + print(pubkey) +def test_call(): + caller = Operator('marx3') #Caller('marx') + # caller.boot(create=True) + # print(caller.keychain()) + phone = TheTelephone(caller=caller) + res = phone.req({'name':'marx', 'pubkey_is_public':True}) + print(res) if __name__ == '__main__': #run_forever() - test_op() \ No newline at end of file + # test_op() + # init_operators() + test_call() \ No newline at end of file diff --git a/komrade/operators/run_op.py b/komrade/operators/run_op.py new file mode 100755 index 0000000..f724c2c --- /dev/null +++ b/komrade/operators/run_op.py @@ -0,0 +1 @@ +from operators import run_forever; run_forever() \ No newline at end of file