posting
quadrismegistus 4 years ago
parent 46160148f7
commit 36e0704062

@ -101,7 +101,23 @@ All simple object-oriented stuff in Python. The database is a simple key-value s
### As developer
The usual installation:
#### 1) Make sure pyenv is installed
[Pyenv](https://github.com/pyenv) is a virtual environment manager that helps you keep track of Python versions. Komrade requires exactly 3.7, for idiosyncracies owing to Kivy.
You can install pyenv using the [auto installer](https://github.com/pyenv-installer):
```
curl https://pyenv.run | bash
```
Then add pyenv to your $PATH (replace .bashrc with .bash_profile if on Mac):
```
echo 'export PATH="~/.pyenv/bin:$PATH"' >> ~/.bashrc
```
#### 2) Clone and bootstrap
Now that pyenv is installed, just clone and bootstrap:
```
git clone https://github.com/quadrismegistus/Komrade.git
@ -109,18 +125,23 @@ cd Komrade
. script/bootstrap
```
~~Then run the client:~~ (this is broken temporarily)
```
cd client
./run.sh
```
### Usage
#### Terminal interface
Run the terminal client:
To run Komrade using the terminal interfae:
```
cd [path to Komrade repo]
python komrade/cli/cli.py
```
### As user
#### Mobile interface
~~To run Komrade using the mobile interfae~~ (this is broken at the moment):
```
cd [path to Komrade repo]
python komrade/app/main.py
```
Coming soon.

@ -31,15 +31,6 @@ class KomradeKey(ABC,Logger):
class KomradeSymmetricKey(KomradeKey):
@property
def cell(self):
if not hasattr(self,'_cell'):
# if hasattr(self,'passphrase') and self.passphrase:
# self._cell = SCellSeal(passphrase=hasher(self.passphrase))
# elif hasattr(self,'key') and self.key:
# self._cell = SCellSeal(key=self.key)
self._cell = SCellSeal(key=self.key)
return self._cell
def encrypt(self,msg,**kwargs):
if hasattr(msg,'data'): msg=msg.data
# print('??? dec',msg,kwargs)
@ -104,6 +95,11 @@ class KomradeSymmetricKeyWithoutPassphrase(KomradeSymmetricKey):
@property
def data(self): return self.key
def __repr__(self): return f'[Symmetric Key]\n ({self.discreet})'
@property
def cell(self):
if not hasattr(self,'_cell'):
self._cell = SCellSeal(key=self.key)
return self._cell
@ -299,50 +295,10 @@ class Keymaker(Logger):
keychain[keyname] = get_key_obj(keyname,keyval)
return keychain
def find_pubkey_and_name(self,name=None,pubkey=None):
if not pubkey: pubkey = self._keychain.get('pubkey')
if not name: name = self.name
if pubkey:
# print('??',pubkey)
if hasattr(pubkey,'data'):
pubkey=pubkey.data_b64
else:
pubkey=b64enc(pubkey)
# if name and pubkey:
# make sure they match
#assert self.find_pubkey(name) == self.find_name(pubkey)
# if self.find_pubkey(name) != self.find_name:
# pass
# self.log(f'! {name} and {pubkey} do not match?')
if name and not pubkey:
pubkey = self.find_pubkey(name)
elif pubkey and not name:
self.name = self.find_name(pubkey)
# else:
# self.log('error! Neither name nor pubkey! Who am I?')
# return (None,None)
if pubkey:
# self.log('!?!?!?!?',type(pubkey),pubkey)
pubkey = b64dec(pubkey)
pubkey=KomradeAsymmetricPublicKey(pubkey)
self._keychain['pubkey'] = pubkey
self.name = name
self.log('found for name and pubkey',name,pubkey)
return (name,pubkey)
def keychain(self,look_for=KEYMAKER_DEFAULT_ALL_KEY_NAMES):
# load existing keychain
keys = self._keychain
# make sure we have the pubkey
# name,pubkey = self.find_pubkey_and_name()
# get uri
pubkey = self.find_pubkey()
if pubkey:
@ -410,7 +366,6 @@ class Keymaker(Logger):
self._uri_id = pubkey.data_b64
return self._uri_id
### BASE STORAGE
@property
def crypt_keys(self):
@ -418,147 +373,14 @@ class Keymaker(Logger):
self._crypt_keys = Crypt(fn=self.path_crypt_keys)
return self._crypt_keys
@property
def crypt_keys_mem(self):
if not hasattr(self,'_crypt_keys_mem'):
self._crypt_keys_mem = CryptMemory()
return self._crypt_keys_mem
@property
def crypt_data(self):
if not hasattr(self,'_crypt_data'):
self._crypt_data = Crypt(fn=self.path_crypt_data)
return self._crypt_data
def can_log_in(self):
if not self.pubkey: return False
if not (self.privkey or self.privkey_encr): return False
return True
### CREATING KEYS
def get_new_keys(self):
raise KomradeException('Every keymaker must make their own get_new_keys() !')
def gen_keys_from_types(self,key_types=KEYMAKER_DEFAULT_KEY_TYPES,passphrase=DEBUG_DEFAULT_PASSPHRASE):
"""
Get new asymmetric/symmetric keys, given a dictionary of constants describing their type
"""
# print('bbbbb')
asymmetric_pubkey=None
asymmetric_privkey=None
keychain = {}
self.log('got key types:',dict_format(key_types))
# gen keys requested
for key_name,key_class in key_types.items():
## asymmetric?
if issubclass(key_class,KomradeAsymmetricKey):
if not asymmetric_privkey or asymmetric_pubkey:
asymmetric_keys = KomradeAsymmetricKey()
asymmetric_pubkey = asymmetric_keys.pubkey_obj
asymmetric_privkey = asymmetric_keys.privkey_obj
if key_class == KomradeAsymmetricPublicKey:
keychain[key_name]=asymmetric_pubkey
elif key_class == KomradeAsymmetricPrivateKey:
keychain[key_name]=asymmetric_privkey
elif key_class == KomradeSymmetricKeyWithPassphrase:
keychain[key_name] = KomradeSymmetricKeyWithPassphrase(passphrase)
else:
# print('??',key_name,key_class)
keychain[key_name] = key_class(None)
self.log('keytypes -> keychain',dict_format(keychain))
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,
save_keychain=True,
return_keychain=True,
return_all_keys=False):
# 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 k])
if not name: name=self.name
# show user what's happening
self.log(f'''
Keymaker ({self}) is forging new keys for {name}
''' + (f'''
* I will save these keys in this crypt: {', '.join(keys_to_save)}
''' if save_keychain else '') #+ #'''
# * I will also save this user's pubkey (as b64 URI) to:
# {self.get_path_qrcode(name=name)}
# ''' + (f'''
+ (f'''
* I will return these keys to you: {', '.join(keys_to_return)}
''' if return_keychain else '')
+ f'''
* I will forge these keys for you: {', '.join(keys_to_gen)}
* I will 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!
self.log('I built this keychain v1!',dict_format(keychain,tab=2))
keychain = self.disassemble(keychain,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!
if save_keychain:
# 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(f'Trying to save these keys ({keys_to_save}), I saved this keychain:',dict_format(keys_saved_d,tab=2),'using the generated-from-pubkey URI ID',uri_id)
# return keys!
if return_all_keys:
return keychain
if return_keychain:
keys_returned = self.return_keychain(keychain,keys_to_return)
self.log('I am returning this keychain:',dict_format(keys_returned,tab=2))
# return (uri_id,keys_returned)
return keys_returned
raise KomradeException('What did you want me to do here?')
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
@ -625,7 +447,7 @@ Keymaker ({self}) is forging new keys for {name}
return (uri_id,keys_saved_d,keychain)
def assemble(self,keychain,passphrase=None,key_types=KEYMAKER_DEFAULT_KEY_TYPES,decrypt=True):
def assemble(self,keychain,key_types=KEYMAKER_DEFAULT_KEY_TYPES,decrypt=True):
encr_keys = [k for k in keychain.keys() if k.endswith('_encr')]
for encr_key_name in encr_keys:
decr_key_name = encr_key_name[:-5] + '_decr'
@ -634,11 +456,8 @@ Keymaker ({self}) is forging new keys for {name}
if decrypt and unencr_key_name in keychain: continue
if not decr_key_name in keychain:
self.log('! not in keychain: decr key name:',decr_key_name,keychain)
#keychain[decr_key_name] = KomradeSymmetricKeyWithPassphrase(passphrase=passphrase)
continue
decr_key = keychain.get(decr_key_name)
# self.log('?',decr_key,decr_key_name,encr_key_name,keychain[encr_key_name])
try:
if decrypt:
encr_key = keychain.get(encr_key_name)
@ -660,9 +479,5 @@ Keymaker ({self}) is forging new keys for {name}
return self.assemble(keychain,decrypt=False,**kwargs)
if __name__ == '__main__':
keymaker = Keymaker('marx69')
keychain = keymaker.forge_new_keys()
\
print(keychain)

@ -27,7 +27,7 @@ class KomradeX(Caller):
# yes? -- login
keys = self.keychain()
self.log(f'booting {self}!',dict_format(keys))
# self.log(f'booting {self}!',dict_format(keys))
if keys.get('pubkey') and keys.get('privkey'):
# self.log('already booted! @'+self.name)

@ -33,7 +33,6 @@ class TheOperator(Operator):
"""
super().__init__(
name,
passphrase,
path_crypt_keys=PATH_CRYPT_OP_KEYS,
path_crypt_data=PATH_CRYPT_OP_DATA
)

@ -1,920 +0,0 @@
ART_TELEPHONE = '''
..--""""----..
.-" ..--""""--.j-.
.-" .-" .--.""--..
.-" .-" ..--"-. \\/ ;
.-" .-"_.--..--"" ..--' "-. :
.' .' / `. \\..--"" __ _ \\ ;
:.__.-" \\ / .' ( )"-. Y
; ;: ( ) ( ). \\
.': /:: : \\ \\
.'.-"\\._ _.-" ; ; ( ) .-. ( ) \\
" `.""" .j" : : \\ ; ; \\
bug /"""""/ ; ( ) "" :.( ) \\
/\\ / : \\ \\`.: _ \\
: `. / ; `( ) (\\/ :" \\ \\
\\ `. : "-.(_)_.' t-' ;
\\ `. ; ..--":
`. `. : ..--"" :
`. "-. ; ..--"" ;
`. "-.:_..--"" ..--"
`. : ..--""
"-. : ..--""
"-.;_..--""
'''
ART_PHONE_SM1 = """
.----------------.
/ _H______H_ \\@,
\\____/ \\____/ @,
/ \\ `@
| LI LI LI | ,@
| LI LI LI | ,@'
| LI LI LI | ,@'
| LI LI LI |@@'
jgs \\ /'
`----------'
"""
ART_ROTARY2="""
_______________
/ \\
| .---------. |@
'---' .-----. '---'@
.' /6 5_4 3\\ '. @
| |7 /...\\ 2| | @
| |8 \\___/ 1| | @
| \\_9_0_)\\/ | @@
/==|_____________|@@@@
H-------------------@@
H ) || || ( @@
H / || || \\ @
H |----''---''----|
=/ |_______________|
"""
ART_KEY = """
8 8 8 8 ,ooo.
8a8 8a8 oP b
d888a888zzzzzzzzzzzzzzzzzzzz8 8b
`""^""' o___oP'
"""
ART_OLDPHONE = """
__
/` _`\\
| (_()| .-.
\\_ _/_/ \\
||=[_] |
|| | | |
||/ \\ |
||`---' /
.--'||-.___.'
/` .-||-.
'-/`.____.`\\
jgs '.______.'
"""
ART_OLDPHONE2="""
_|~|/|
( | | |
/_|_|\\|
| |
| |~|
| | |
| | |
| |-|
| | \\
| |__|
|_|_
/ ~-_
/ ~-_
|___________|
"""
ART_ROTARY = """
_...----..._
,-' ,-. `-.
,' ,-. ( 4 ) ,-. `.
,' ( 5 ) `-' ( 3 ) `.
/ ,-. `-',-'' ``-.`-' ,-. \\
/ ( 6 ) ,' `. ( 2 ) \\
: `-' / FEUER \\ `-' :
| ,-. : ________ : ,-. |
|( 7 ) | |________| | ( 1 )|
| `-' : ; `-' |
: ,-. \\ NOTRUF / ;
\\ ( 8 ) `. ,'(`. /
\\ `-' ,-.`-..__..-' \\ `-./
`. ( 9 ) ,-. \\ ,'
`. `-' ( 0 ) ,'`
`-._ `-' _.-'
```----''' SSt
"""
ART_PHONE_DIAGRAM = """
________
.' / / )
/ /##/ /|
/ `--' / |
/__ __ __ / |
//_//_//_// / __
//_//_//_// / \\`.___ Listening end
//_//_//_// /
//_//_//_// /__
/ / / \\`.___ Buttons
/ .-. / /
/ /#/ / /
/ `-' / /__
/ .====. / / \\`.___ Speaking end
|`--------' /
\\ , .'__
`-//----' \\`.___ Disconnect button
//
"""
ART_OLDPHONE3 = """
__ _
.: .' '.
/: / \\_
;: ; ,-'/`:\\
|: | | |():|
;: ; '-.\\_:/
\\: \\ /`
':_'._.'
||
/__\\
.---. {====}
.' _,"-,__|:: |
/ ((O)=;--.:: |
; `|: | |:: |
| |: | |:: |
| |: | |:: |
| |: | |:: |
| |: | |:: |
| |: | |:: |
| /:'__\\ |:: |
| [______]|:: |
| `----` |:: |__
| _.--|:: | ''--._
; .' __{====}__ '.
\\ .'_.-'._ `""` _.'-._ '.
'--'/` `''''` `\\ '.__
jgs '._ _.'
`""--......--""`
"""
ART_OLDPHONE4 = """
__
/` _`\\
| (_()| .-.
\\_ _/_/ \\
||=[_] |
|| | | |
||/ \\ |
||`---' /
.--'||-.___.'
/` .-||-.
'-/`.____.`\\
'.______.'
"""
# by jgs
ART_PAYPHONE = """
_________________
/ __ \\
| (__) |
| |
| .-----. .--. |
| | | / \\ |
| '-----' \\ / |
| | | |
| LI LI LI | | |
| LI LI LI | | |Oo
| LI LI LI | | |`Oo
| LI LI LI | | | Oo
| | | | Oo
| .------. / \\ | oO
| | | \\ / | Oo
| '------' '-oO | oO
| .---Oo | Oo
| || ||`Oo oO
| |'--'| | OoO
| '----' |
jgs \\_________________/
"""
ART_HAMMER = """
,
/( ___________
| >:===========`
)(
""
"""
ART_KEY_PAIR = """
__
/o \\_____
\__/-="="`
__
/ o\\
\_ /
<|
<|
<|
`
"""
ART_KEY_PAIR2 = """
__
/o \\_____
\__/-="="`
__ (public)
/ o\\
\_ /
<|
<|
<| (private)
"""
ART_KEY_PAIR2A = """
__
/o \\_____
\__/-="="`
__ (1) public key
/ o\\
\_ /
<|
<|
<|
"""
ART_KEY_PAIR2B = """
__
/o \\_____
\__/-="="`
__ (1) public key
/ o\\
\_ /
<|
<|
<| (2) private key
"""
ART_KEY_PAIR_SPLITTING1 = """
_
/o
\
<
<
<
(2A)
symmetric
encryption
key, from
hashed
passphrase
"""
ART_KEY_PAIR_SPLITTING2 = """
_ __
/o / o\\
\ \_ /
< --(encrypts)--> <|
< <|
< <|
(2A) (2)
symmetric asymmetric
encryption private
key, from key
hashed
passphrase
"""
ART_KEY_PAIR_SPLITTING3 = """
_ __ _
/o / o\\ \\
\ \_ / /
< --(encrypts)--> <| --(into)--> |
< <| |
< <| |
(2A) (2) (2B)
symmetric asymmetric encrypted form
encryption private of (2)
key, from key
hashed
passphrase
"""
ART_KEY_PAIR_SPLITTING4 = """
_ _
/o \\
\ /
< |
< |
< |
(2A) (2B)
symmetric encrypted form
encryption of (2)
key, from
hashed
passphrase
"""
ART_KEY_PAIR_SPLITTING5 = """
_
\\
/
|
|
|
(2B)
encrypted form
of (2)
"""
ART_KEY_PAIR32 = """
__
/o \\_____
\__/-="="`
_ _ (1) public key
/o \\
\ _ /
< |
< |
< | (2B) privkey_encr
(2A) privkey_decr
"""
ART_KEY_PAIR3A2 = """
__
/o \\_____
\__/-="="`
_ _ (1) public key
/o \\
\ _ /
< |
< |
< |
"""
ART_KEY_KEY2A = """ _
/o
\
<
<
<
"""
ART_KEY_PAIR3B2 = """
__
/o \\_____
\__/-="="`
_ _
/o \\
\ _ /
< |
< |
< | (2B) privkey_encr
"""
ART_KEY_PAIR3C2 = """
__
/o \\_____
\__/-="="`
_ _ (1) public key
/o \\
\ _ /
< |
< |
< | (2B) privkey_encr
(2A) privkey_decr
"""
ART_KEY_PAIR3 = """
__
/o \\_____
\__/-="="`
_ _ (1) public key
/o \\
\ _ /
< |
< |
< | (2B) privkey_encr
(2A) privkey_decr
"""
ART_KEY_PAIR4 = """
__
/o \\_____
\__/-="="`
_
\\
_ /
|
|
| (2B) privkey_encr
(2A) privkey_decr
"""
ART_KEY_PAIR4Z = """
__
/o \\_____
\__/-="="`
_
\\
_ /
|
|
|
(2A) privkey_decr
"""
ART_KEY_PAIR4D = """
_
\\
_ /
|
|
| (2B) privkey_encr
(2A) privkey_decr
"""
ART_KEY_PAIR4C = """
_
\\
_ /
|
|
|
(2A) privkey_decr
"""
ART_KEY_PAIR4B = """ _
\\
_ /
|
|
|
"""
ART_KEY_PAIR31A = """
__
/o \\_____
\__/-="="`
(1) public key"""
ART_KEY_PAIR3A = """
__
/o \\_____
\__/-="="`"""
ART_KEY_PAIR3B = """ _ _
/o \\
\ _ /
< |
< |
< |
(2A) (2B) encrypted
encryption form of (2)
key for (2B)
"""
ART_KEY_PAIR5 = """
__
/o \\_____
\__/-="="`
(1) public key
_ _
/o \\
\ _ /
< |
< |
< |
(2A) (2B) encrypted
encryption form of (2)
key for (2B)
"""
ART_KEY_PAIR4Z1 = """
__
/o \\_____
\__/-="="`
(1) public key
_
\\
_ /
|
|
|
(2B) encrypted
form of (2)
"""
ART_KEY_PAIR4ZZ = """
__
/o \\_____
\__/-="="`
_
\\
_ /
|
|
|
"""
ART_KEY_PAIR4Z2 = """
__
/o \\_____
\__/-="="`
(1) public key
"""
# """
ART_KEY_PAIR4Z3 = """
__
/o \\_____
\__/-="="`
(1) public key
"""
ART_KEY_PAIR4Z3 = """
__
/o \\_____
\__/-="="`
(1) public key
"""
ART_KEY_PAIR4Z42 = """
"""
ART_KEY_PAIR4Z4B = """
__
/ o\\
\_ /
<|
<|
<|
"""
ART_KEY_PAIR4Z4 = """
__
/ o\\
\_ /
<|
<|
<|
"""
ART_KEY_PAIR3BB = """ _
\\
_ /
|
|
|
(2A) (2B) encrypted
encryption form of (2)
key for (2B)
"""
ART_KEY_PAIR3AA = """ _ _
/o \\
\ _ /
< |
< |
< |
"""
ART_KEY_PAIR_SEP = """
__
/o \\_____
\__/-="="`
__
/ o\\
\_ /
<|
<|
<|
`
"""
ART_KEY_CHAIN = """
___________ @ @
/ (@\\ @
\___________/ _@
@ _/@ \\_____
@/ \__/-="="`
\_ /
<|
<|
<|
`
"""
ART_FROG_BLENDER ="""
___
_______|___|______
__|__________________|
\ ]________________[ `---.
`. ___ L
| _ | L |
| .'_`--.___ __ | | |
|( 'o` - .`.'_ ) | F F
| `-._ `_`./_ | / /
J '/\\ ( .'/ )F.' /
L ,__//`---'`-'_/J .'
J /-' '/ F.'
L ' J'
J `.`-. .-'.' F
L `.-'.-' J
|__(__(___)__|
F J
J L
|______________|
"""
####
# code
###
from PIL import Image
ASCII_CHARS = [ '#', ' ', '%', '.', 'S', '+', '.', '*', ':', ',', '@']
def scale_image(image, new_width=100):
"""Resizes an image preserving the aspect ratio.
"""
(original_width, original_height) = image.size
aspect_ratio = original_height/float(original_width)
new_height = int(aspect_ratio * new_width)
new_image = image.resize((new_width, new_height))
return new_image
def convert_to_grayscale(image):
return image.convert('L')
def map_pixels_to_ascii_chars(image, range_width=25):
"""Maps each pixel to an ascii char based on the range
in which it lies.
0-255 is divided into 11 ranges of 25 pixels each.
"""
pixels_in_image = list(image.getdata())
pixels_to_chars = [ASCII_CHARS[pixel_value//range_width] for pixel_value in
pixels_in_image]
return "".join(pixels_to_chars)
def convert_image_to_ascii(image, new_width=100):
image = scale_image(image)
image = convert_to_grayscale(image)
pixels_to_chars = map_pixels_to_ascii_chars(image)
len_pixels_to_chars = len(pixels_to_chars)
image_ascii = [pixels_to_chars[index: index + new_width] for index in
range(0, len_pixels_to_chars, new_width)]
return "\\n".join(image_ascii)
def handle_image_conversion(image_filepath):
image = None
try:
image = Image.open(image_filepath)
except Exception as e:
# print "Unable to open image file {image_filepath}.".format(image_filepath=image_filepath)
# print e
return
image_ascii = convert_image_to_ascii(image)
print(image_ascii)
if __name__=='__main__':
import sys
image_file_path = sys.argv[1]
handle_image_conversion(image_file_path)

@ -1,415 +0,0 @@
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 *
import art
import textwrap as tw
class CLI(Logger):
ROUTES = {
'help':'see help messages',
'register':'register new user',
'login':'log back in'
}
def __init__(self,name='',cmd='',persona=None):
self.name=name
self.cmd=cmd
self.persona=persona
def run(self,inp='',name=''):
self.name=name
clear_screen()
# self.boot()
# self.help()
if inp: self.route(inp)
while True:
try:
inp=input(f'@{self.name if self.name else "?"}: ')
except KeyboardInterrupt:
exit()
self.route(inp)
#await asyncio.sleep(0.5)
def route(self,inp):
inp=inp.strip()
if not inp.startswith('/'): return
cmd=inp.split()[0]
dat=inp[len(cmd):].strip()
cmd=cmd[1:]
if cmd in self.ROUTES and hasattr(self,cmd):
f=getattr(self,cmd)
return f(dat)
def boot(self,indent=5):
logo=art.text2art(CLI_TITLE,font=CLI_FONT)
# logo=make_key_discreet_str(logo,chance_redacted=0.1) #.decode()
logo=tw.indent(logo, ' '*indent)
scan_print(logo,max_pause=0.005)
def help(self):
print()
for cmd,info in self.ROUTES.items():
print(f' /{cmd}: {info}')
# print('\n')
print('\n')
def intro(self):
self.status(None,)
def register(self,dat):
self.komrade = Komrade(self.name)
self.persona.register()
### DIALOGUES
# hello, op?
def status_keymaker_part1(self,name):
self.status(None,{ART_OLDPHONE4+'\n',True},3) #,scan=False,width=None,pause=None,clear=None)
nm=name if name else '?'
self.status(
f'\n\n\n@{nm}: Uh yes hello, Operator? I would like to join Komrade, the socialist network. Could you patch me through?',clear=False)
while not name:
name=self.status(('name','@TheTelephone: Of course, Komrade...?\n@')).get('vals').get('name').strip()
print()
self.status(
f'@TheTelephone: Of course, Komrade @{name}. A fine name.',
'''@TheTelephone: However, I'm just the local operator who lives on your device; my only job is to communicate with the remote operator securely.''',
'''Komrade @TheOperator lives on the deep web. She's the one you want to speak with.''',
None,{ART_OLDPHONE4},f'''@{name}: Hm, ok. Well, could you patch me through to the remote operator then?''',
f'''@{TELEPHONE_NAME}: I could, but it's not safe yet. Your information could be exposed. You need to cut your encryption keys first.''',
f'@{name}: Fine, but how do I do that?',
f'@{TELEPHONE_NAME}: Visit the Keymaker.',
clear=False,pause=True)
### KEYMAKER
self.status(None,{tw.indent(ART_KEY,' '*5)+'\n',True},3) #,clear=False,indent=10,pause=False)
# convo
self.status(
f'\n@{name}: Hello, Komrade @Keymaker? I would like help forging a new set of keys.',
f'@Keymaker: Of course, Komrade @{name}.',
)
self.status(
'I will cut for you two matching keys, part of an "asymmetric" pair.',
'Please, watch me work.',
None,{tw.indent(ART_KEY,' '*5)+'\n'},
'I use a high-level cryptographic function from Themis, a well-respected open-source cryptography library.',
'I use the iron-clad Elliptic Curve algorthm to generate the asymmetric keypair.',
'> GenerateKeyPair(KEY_PAIR_TYPE.EC)',
3
)
self.status(
None,
{ART_KEY_PAIR,True}
) #,clear=False,indent=10,pause=False)
return name
def status_keymaker_part2(self,name,passphrase,pubkey,privkey,hasher,persona):
from getpass import getpass
# gen what we need
uri_id = pubkey.data_b64
qr_str = get_qr_str(uri_id)
qr_path = os.path.join(PATH_QRCODES,name+'.png')
# what are pub/priv?
# self.status(
# None,{ART_KEY_PAIR},
# 'A matching set of keys have been generated.',
# None,{ART_KEY_PAIR2A+'\nA matching set of keys have been generated.'+'\n'},
# 'First, I have made a "public key" which you can share with anyone:',
# f'(1) {pubkey.data_b64.decode()}',
# 'This key is a randomly-generated binary string, which acts as your "address" on Komrade.',
# 'With it, someone can write you an encrypted message which only you can read.'
# )
# self.status(
# None,{ART_KEY_PAIR2A},
# f'You can share your public key by copy/pasting it to them over a secure channel (e.g. Signal).',
# 'Or, you can share it as a QR code, especially IRL:',
# {qr_str+'\n\n',True,5},
# f'\n\n(If registration is successful, this QR code be saved as an image to your device at: {qr_path}.)'
# )
# private keys
self.status(None,
{ART_KEY_PAIR2B},
'Second, I have cut a matching "private key".',
"It's too dangerous to show in full, so I've redacted most of it:",
f'(2) {make_key_discreet(privkey.data_b64,0.3)}',
'With it, you can decrypt any message sent to you via your public key.',
'You can also encrypt messages ',
)
# private keys
self.status(None,
{CUBEKEY},
'You you should never, ever give your private key to anyone.',
'In fact, this key is so dangerous that we need to lock it away immediately.',
"We'll even throw away the key we use to lock this private key with!",
"How? By regenerating it each time from your password.",
)
if not passphrase:
from getpass import getpass
self.status(
'And it looks like you haven\'t yet chosen a password.',
3,"Don't tell it to me! Never tell it to anyone.",
"Ideally, don't even save it on your computer; just remember it, or write it down on paper.",
"Instead, whisper it to Komrade @Hasher, who scrambles information '1-way', like a blender.",
)
res = self.status(None,
{indent_str(ART_FROG_BLENDER,10),True},
"@Keymaker: Go ahead, try it. Type anything to @Hasher.",
('str_to_hash',f'@{name}: ',input)
)
str_to_hash = res.get('vals').get('str_to_hash')
hashed_str1 = hasher(str_to_hash.encode())
res = self.status(
'@Hasher: '+hashed_str1
)
res = self.status(
'@Keymaker: Whatever you typed, there\'s no way to reconstruct it from that garbled mess.',
'But whatever you typed will always produce the *same* garbled mess.',
('str_to_hash',f'Try typing the exact same thing over again:\n@{name}: ',input)
)
str_to_hash = res.get('vals').get('str_to_hash')
hashed_str2 = hasher(str_to_hash.encode())
res = self.status(
'@Hasher: '+hashed_str2
)
if hashed_str1==hashed_str2:
self.status('See how the hashed values are also exactly the same?')
else:
self.status('See how the hashed values have also changed?')
res = self.status(
('str_to_hash',f'Now try typing something just a little bit different:\n@{name}: ',input)
)
str_to_hash = res.get('vals').get('str_to_hash')
hashed_str3 = hasher(str_to_hash.encode())
res = self.status(
'@Hasher: '+hashed_str3
)
if hashed_str2==hashed_str3:
self.status('See how the hashed values are also the same?')
else:
self.status('See how the hashed values have also changed?')
self.status(
None,{indent_str(ART_FROG_BLENDER,10)},
'@Keymaker: Behind the scenes, @Hasher is using the SHA-256 hashing function, which was designed by the NSA.',
'But @Hasher also adds a secret "salt" to the recipe, as it\'s called.',
'To whatever you type in, @Hasher adds a secret phrase: another random string of characters which never changes.',
"By doing so, the hash output is \"salted\": made even more idiosyncratic to outside observers.",
)
self.status(
None,{indent_str(ART_FROG_BLENDER,10)},
f"I've taken the liberty of generating a random secret for your device, which I show here mostly redacted:",
make_key_discreet_str(persona.crypt_keys.secret.decode(),0.25),
'The full version of this secret is silently added to every input you type into @Hasher.',
"I've saved this secret phrase to a hidden location on your device hardware.",
)
self.status(
None,{indent_str(ART_FROG_BLENDER,10)},
'However, this means that you will be unable to log in to your account from any other device.',
'This limitation provides yet another level of hardware protection against network attacks.',
'However, you can always choose (not recommended) to the secret file with another device by a secure channel.',
3,f'But, please excuse me Komrade @{name} -- I digress.'
)
while not passphrase:
res = self.status(None,
{indent_str(ART_FROG_BLENDER,10)},
"@Keymaker: Please type your chosen password into @Hasher.",
('str_to_hash',f'\n@{name}: ',getpass),
pause=False
)
str_to_hash = res.get('vals').get('str_to_hash')
hashed_pass1 = hasher(str_to_hash.encode())
res = self.status(
'\n@Hasher: '+hashed_pass1,
pause=False
)
res = self.status(
'\nNow type in the same password one more time to verify it:',
('str_to_hash',f'\n@{name}: ',getpass),
pause=False
)
str_to_hash = res.get('vals').get('str_to_hash')
hashed_pass2 = hasher(str_to_hash.encode())
res = self.status(
'\n@Hasher: '+hashed_pass2,
pause=False
)
if hashed_pass1==hashed_pass2:
self.status('','@Keymaker: Excellent. The passwords clearly matched, because the hashed values matched.',pause=False)
passphrase = hashed_pass1
else:
self.status('@Keymaker: A pity. It looks like the passwords didn\'t match, since the hashed values didn\'t match either. Try again?')
return passphrase
def status_keymaker_part3(self,privkey,privkey_decr,privkey_encr,passphrase):
self.status(
None,{tw.indent(ART_KEY,' '*5)+'\n',True},
# None,{ART_+'\n',True},
'Now that we have a hashed passphrase, we can generate the (2A) encryption key.',
{ART_KEY_KEY2A,True,0.1},
'''The key is formed using Themis's high-level symmetric encryption library: SecureCell, using Seal mode.''',
'This key (2A) then uses the AES-256 encryption algorithm to encrypt the super-sensitive private key (2):'
)
s0=str.center('[Encryption Process]',CLI_WIDTH)
s1=s0 + '\n\n' + self.printt('Now that we have (2A), we can use it to encrypt the super-sensitive private key (2):',ret=True)
s2a = self.printt(f"(2A) {make_key_discreet_str(passphrase)}",ret=True)
s2 = self.printt(f"(2) {make_key_discreet(privkey.data_b64)}",ret=True)
s2b = self.printt(f"(2B) {make_key_discreet(b64encode(privkey_encr))}",ret=True)
self.status(
# screen 1
None,{f'{s1}'},
False,
# 2
None,{f'{s1}\n\n{ART_KEY_PAIR_SPLITTING1}'},
{s2a,True},
False,
# 3
None,{f'{s1}\n\n{ART_KEY_PAIR_SPLITTING2}\n{s2a}'},
{'\n'+s2,True},
False,
# 4
None,{f'{s1}\n\n{ART_KEY_PAIR_SPLITTING3}\n{s2a}\n\n{s2}'},
{'\n'+s2b,True},
False,
)
shdr=str.center('[Decryption Process]',CLI_WIDTH) + '\n\n' + self.printt('Once we have (2B), we don\'t need (2A) or (2) anymore. We can regenerate them!',ret=True)
from getpass import getpass
passhash = None
while passhash!=passphrase:
res = self.status(
None,{shdr},False if passhash is None else True,
("pass",self.printt(f"Let's try. Re-type your password into @Hasher:",ret=True)+f" \n ",getpass)
)
passhash = self.persona.crypt_keys.hash(res.get('vals').get('pass').encode())
if passhash!=passphrase:
self.status({' Looks like they don\'t match. Try again?'},False)
self.status(
{' Excellent. We can now regenerate the decryption key:'},False,
{s2a,True},False,
)
# self.status('great')
# shdr2=
self.status(
# 2
None,{f'{shdr}\n\n{ART_KEY_PAIR_SPLITTING1}'},
{s2a,True},
False,
# 3
# None,{f'{s1}\n\n{ART_KEY_PAIR_SPLITTING2}\n{s2a}'},
# {'\n'+s2,True},
# False,
# 4
None,{f'{s1}\n\n{ART_KEY_PAIR_SPLITTING4}\n{s2a}\n\n\n\n'},
{'\n'+s2b,True},
False,
)
def run_cli():
cli = CLI()
cli.run('/register','elon') #'/register',name='elon')
if __name__=='__main__':
run_cli()
# asyncio.run(test_async())
"""
Outtakes
self.status(None,
{ART_KEY_PAIR31A},
{ART_KEY_PAIR3B+'\n',True},
3,'Allow me to explain.',
'(2A) is a separate encryption key generated by your password.',
'(2B) is a version of (2) which has been encrypted by (2A).',
"Because (2) will be destroyed, to rebuild it requires decrypting (2B) with (2A).",
)
self.status(
None,{ART_KEY_PAIR5+'\n'},
"However, in a final move, I will now destroy (2A), too.",
None,{ART_KEY_PAIR4Z1+'\n'},
'Why? Because now only you can regenerate it by remembering the password which created it.',
# None,{ART_KEY_PAIR4Z1+'\n'},
'However, this also means that if you lose or forget your password, you\'re screwed.',
None,{ART_KEY_PAIR4Z2+'\n'},
"Because without key (2A),you couldn never unlock (2B).",
None,{ART_KEY_PAIR4Z3+'\n'},
"And without (2B) and (2A) together, you could never re-assemble the private key of (2).",
None,{ART_KEY_PAIR4Z42+'\n'},
"And without (2), you couldn't read messages sent to your public key.",
)
"""
Loading…
Cancel
Save