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.crypt import * from abc import ABC, abstractmethod class KomradeKey(ABC,Logger): @abstractmethod def encrypt(self,msg,**kwargs): pass @abstractmethod def decrypt(self,msg,**kwargs): pass @abstractmethod def data(self): pass @property def data_b64(self):return b64encode(self.data) @property def discreet(self): return make_key_discreet(self.data) class KomradeSymmetricKey(KomradeKey): @property def cell(self): if not hasattr(self,'_cell'): if hasattr(self,'passphrase') and self.passphrase: self._cell = SCellSeal(passphrase=self.passphrase) elif hasattr(self,'key') and self.key: self._cell = SCellSeal(key=self.key) return self._cell def encrypt(self,msg,**kwargs): if issubclass(type(msg), KomradeKey): msg=msg.data return self.cell.encrypt(msg,**kwargs) def decrypt(self,msg,**kwargs): return self.cell.decrypt(msg,**kwargs) def getpass_status(passphrase=None): while not passphrase: passphrase1 = getpass(f'@Keymaker: What is a *memorable* pass word or phrase? Do not write it down.\n@{name}: ') passphrase2 = getpass(f'@Keymaker: Could you repeat that?') if passphrase1!=passphrase2: self.status('@Keymaker: Those passwords didn\'t match. Please try again.',clear=False,pause=False) else: return passphrase1 class KomradeSymmetricKeyWithPassphrase(KomradeSymmetricKey): def __init__(self,passphrase=DEBUG_DEFAULT_PASSPHRASE, why=WHY_MSG): self.passphrase=passphrase if not self.passphrase: self.passphrase=getpass_status if SHOW_LOG else getpass.getpass(why) #return self.passphrase @property def data(self): return KEY_TYPE_SYMMETRIC_WITH_PASSPHRASE.encode('utf-8') def __repr__(self): return f'[Symmetric Key] ({self.discreet})' class KomradeSymmetricKeyWithoutPassphrase(KomradeSymmetricKey): def __init__(self,key=None): self.key = GenerateSymmetricKey() if not key else key @property def data(self): return self.key def __repr__(self): return f'[Symmetric Key] ({self.discreet})' class KomradeAsymmetricKey(KomradeKey): def __init__(self,pubkey,privkey): self.pubkey=pubkey self.privkey=privkey def encrypt(self,msg,pubkey=None,privkey=None): if issubclass(type(msg), KomradeKey): msg=msg.data pubkey=pubkey if pubkey else self.pubkey privkey=privkey if privkey else self.privkey return SMessage(privkey,pubkey).wrap(msg) def decrypt(self,msg,pubkey=None,privkey=None): pubkey=pubkey if pubkey else self.pubkey privkey=privkey if privkey else self.privkey return SMessage(privkey,pubkey).unwrap(msg) @property def data(self): return self.key class KomradeAsymmetricPublicKey(KomradeAsymmetricKey): def __init__(self,pubkey,privkey=None): self.pubkey=pubkey self.privkey=privkey @property def key(self): return self.pubkey @property def data(self): return self.pubkey def __repr__(self): return f'''[Asymmetric Public Key] ({self.data_b64.decode()})''' class KomradeAsymmetricPrivateKey(KomradeAsymmetricKey): def __init__(self,privkey,pubkey=None): self.pubkey=pubkey self.privkey=privkey @property def data(self): return self.privkey @property def key(self): return self.privkey def __repr__(self): return f'''[Asymmetric Private Key] ({self.discreet})''' def make_key_discreet(data,chance_bowdlerize=0.5): import random if not data: return '?' if not isBase64(data): data=b64encode(data) key=data.decode() return ''.join((k if random.random()\n{_key_encr}') keychain[key_name]=_key_encr_obj return keychain def forge_new_keys(self, name=None, passphrase=DEBUG_DEFAULT_PASSPHRASE, keys_to_save = KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_SERVER, keys_to_return = KEYMAKER_DEFAULT_KEYS_TO_SAVE_ON_CLIENT, keys_to_gen = KEYMAKER_DEFAULT_KEYS_TO_GEN, key_types = KEYMAKER_DEFAULT_KEY_TYPES): # setup keys_to_gen = set(keys_to_gen) | set(keys_to_save) | set(keys_to_return) keys_to_gen = sorted(list(keys_to_gen),key=lambda x: x.count('_')) key_types = dict([(k,key_types[k]) for k in keys_to_gen]) if not name: name=self.name print('forging!') # show user what's happening self.log(f''' Keymaker ({self}) is forging new keys for {name} I will save these keys in this crypt: {keys_to_save} I will also save this user's pubkey (as b64 URI) to: {self.get_path_qrcode(name=name)} I will return these keys to you: {keys_to_return} which means I will end up generating these keys: {keys_to_gen} I will also be using these key types to do so: {dict_format(key_types,tab=4)} ''') # gen decryptor keys! keychain = self.gen_keys_from_types(key_types,passphrase=passphrase) # gen encrypted keys! keychain = self.gen_encr_keys(keychain,keys_to_gen,passphrase=passphrase) self.log('I built this keychain!',dict_format(keychain,tab=2)) # self.status('@Keymaker: I ended up building these keys:',keychain) # save keys! # get URI id to save under (except for pubkeys, accessible by name) uri_id,keys_saved_d,keychain = self.save_keychain(name,keychain,keys_to_save) self.log('I saved this keychain:',dict_format(keys_saved_d,tab=2),'using the generated-from-pubkey URI ID',uri_id) # return keys! keys_returned = self.return_keychain(keychain,keys_to_return) self.log('I am returning this keychain:',dict_format(keys_returned,tab=2)) print('done forging!') return (uri_id,keys_returned) def return_keychain(self,keychain,keys_to_return=None): keychain_toreturn = {} if not keys_to_return: keys_to_return = list(keychain.keys()) for key in keys_to_return: if key in keychain: keychain_toreturn[key]=keychain[key] return keychain_toreturn def get_path_qrcode(self,name=None,dir=None,ext='.png'): if not name: name=self.name if not dir: dir = PATH_QRCODES fnfn = os.path.join(dir,name+ext) return fnfn @property def qr(self): return self.qr_str(data=self.uri_id) def qr_str(self,data=None): import qrcode qr=qrcode.QRCode() qr.add_data(self.uri_id if not data else data) ascii = capture_stdout(qr.print_ascii) return ascii def save_uri_as_qrcode(self,uri_id=None,name=None): if not uri_id: uri_id = self.uri_id if not uri_id and not self.uri_id: raise KomradeException('Need URI id to save!') if not name: name=self.name # gen import pyqrcode qr = pyqrcode.create(uri_id) ofnfn = self.get_path_qrcode(name=name) qr.png(ofnfn,scale=5) self._uri_id = uri_id self.log(f'''Saved URI(=pubkey_b64) as a QR code: {ofnfn} {self.qr}''') def save_keychain(self,name,keychain,keys_to_save=None,uri_id=None): if not keys_to_save: keys_to_save = list(keychain.keys()) if not uri_id and 'pubkey' in keychain: uri_id = b64encode(keychain['pubkey'].data).decode() #uri_id = get_random_id() + get_random_id() # self.log(f'SAVING KEYCHAIN FOR {name} under URI {uri_id}') self._uri_id = uri_id # filter for transfer for k,v in keychain.items(): if issubclass(type(v),KomradeKey): v=v.data keychain[k]=v # save keychain keys_saved_d={} for keyname in keys_to_save: if not '_' in keyname and keyname!='pubkey': raise KomradeException('there is no private property in a socialist network! all keys must be split between komrades') if keyname in keychain: # uri = uri_id uri = uri_id if keyname!='pubkey' else name if not uri: raise KomradeException('invalid URI! {uri}') val = keychain[keyname] if issubclass(type(keychain[keyname]), KomradeKey) or issubclass(type(keychain[keyname]), KomradeEncryptedKey): val = val.data self.crypt_keys.set(uri,val,prefix=f'/{keyname}/') keys_saved_d[keyname] = keychain[keyname] # save pubkey as QR if not 'pubkey' in keys_saved_d: # self.log('did not save pubkey in crypt, storing as QR...') self.save_uri_as_qrcode(name=name, uri_id=uri_id) # set to my keychain right away self._keychain = keychain return (uri_id,keys_saved_d,keychain) def assemble(self,_keychain): # last minute assemblies? encr_keys = [k for k in _keychain if k.endswith('_encr')] for ekey in encr_keys: eval=_keychain[ekey] if not eval: continue unencrkey = ekey[:-len('_encr')] if unencrkey in _keychain: continue decrkey = unencrkey+'_decr' if decrkey not in _keychain: continue dval=_keychain[decrkey] if not dval: continue # self.log(ekey,decrkey,'??') # self.log(eval,dval,'????') new_val = self.assemble_key(eval,dval) # self.log('!!#!',new_val) if new_val: _keychain[unencrkey] = new_val return _keychain def assemble_key(self, key_encr, key_decr, key_encr_name=None, key_decr_name=None): # self.log(f'assembling key: {key_decr} decrypting {key_encr}') # need the encrypted half if not key_encr: # self.log('!! encrypted half not given') return if not key_decr: if self.passphrase: key_decr = self.passphrase else: # self.log('!! decryptor half not given') return # need some way to regenerate the decryptor decr_cell = self.get_cell(key_decr) # need the decryptor half if not decr_cell: # self.log('!! decryptor cell not regenerable') return # decrypt! try: # self.log(f'>> decrypting {key_encr_name} with {key_decr_name}\n({key_encr} with cell {decr_cell}') key = decr_cell.decrypt(key_encr) # self.log('assembled_key built:',key) return key except ThemisError as e: # self.log('!! decryption failed:',e) return def get_cell(self, str_or_key_or_cell): # self.log('getting decr cell for',str_or_key_or_cell) if type(str_or_key_or_cell)==SCellSeal: return str_or_key_or_cell elif type(str_or_key_or_cell)==str: return SCellSeal(passphrase=str_or_key_or_cell) elif type(str_or_key_or_cell)==bytes: return SCellSeal(key=str_or_key_or_cell) # def keychain(self, # passphrase=DEBUG_DEFAULT_PASSPHRASE, # extra_keys={}, # keys_to_gen=KEYMAKER_DEFAULT_KEYS_TO_GEN, # uri_id=None, # **kwargs): # # assemble as many keys as we can! # self.log(f'''keychain( # passphrase={passphrase}, # extra_keys={extra_keys}, # keys_to_gen={keys_to_gen}, # uri_id={uri_id}, # **kwargs = {kwargs} # )''') # if not uri_id: uri_id = self.uri_id # if not uri_id and not self.uri_id: # raise KomradeException('Need URI id to complete finding of keys!') # self.log('getting keychain for uri ID:',uri_id) # # if not force and hasattr(self,'_keychain') and self._keychain: return self._keychain # if passphrase: self.passphrase=passphrase # # start off keychain # _keychain = {**extra_keys, **self._keychain} # self.log('_keychain at start of keychain() =',_keychain) # # find # for keyname in keys_to_gen: # if keyname in _keychain and _keychain[keyname]: continue # # self.log('??',keyname,keyname in self._keychain,'...') # newkey = self.crypt_keys.get(uri_id,prefix=f'/{keyname}/') # if newkey: _keychain[keyname] = newkey # # return # _keychain = self.assemble(_keychain) # self._keychain = _keychain # return _keychain return _keychain if __name__ == '__main__': keymaker = Keymaker('marx69') keychain = keymaker.forge_new_keys() print(keychain)