keys-on-cli
quadrismegistus 4 years ago
parent 832c82953e
commit 72d6821556

@ -52,8 +52,8 @@ def getpass_status(passphrase=None):
else: else:
return passphrase1 return passphrase1
get_pass_func = getpass_status if SHOW_STATUS else getpass # get_pass_func = getpass_status if SHOW_STATUS else getpass
from getpass import getpass
class KomradeSymmetricKeyWithPassphrase(KomradeSymmetricKey): class KomradeSymmetricKeyWithPassphrase(KomradeSymmetricKey):
def __init__(self,passphrase=DEBUG_DEFAULT_PASSPHRASE, why=WHY_MSG): def __init__(self,passphrase=DEBUG_DEFAULT_PASSPHRASE, why=WHY_MSG):
@ -63,14 +63,14 @@ class KomradeSymmetricKeyWithPassphrase(KomradeSymmetricKey):
#return self.passphrase #return self.passphrase
@property @property
def data(self): return KEY_TYPE_SYMMETRIC_WITH_PASSPHRASE.encode('utf-8') def data(self): return KEY_TYPE_SYMMETRIC_WITH_PASSPHRASE.encode('utf-8')
def __repr__(self): return f'[Symmetric Key] ({self.discreet})' def __repr__(self): return f'[Symmetric Key] (generated by password)'
class KomradeSymmetricKeyWithoutPassphrase(KomradeSymmetricKey): class KomradeSymmetricKeyWithoutPassphrase(KomradeSymmetricKey):
def __init__(self,key=None): def __init__(self,key=None):
self.key = GenerateSymmetricKey() if not key else key self.key = GenerateSymmetricKey() if not key else key
@property @property
def data(self): return self.key def data(self): return self.key
def __repr__(self): return f'[Symmetric Key] ({self.discreet})' def __repr__(self): return f'[Symmetric Key]\n ({self.discreet})'
@ -106,7 +106,7 @@ class KomradeAsymmetricPublicKey(KomradeAsymmetricKey):
@property @property
def data(self): return self.pubkey def data(self): return self.pubkey
def __repr__(self): return f'''[Asymmetric Public Key] ({self.data_b64.decode()})''' def __repr__(self): return f'''[Asymmetric Public Key]\n ({self.data_b64.decode()})'''
class KomradeAsymmetricPrivateKey(KomradeAsymmetricKey): class KomradeAsymmetricPrivateKey(KomradeAsymmetricKey):
def __init__(self,privkey,pubkey=None): def __init__(self,privkey,pubkey=None):
self.pubkey=pubkey self.pubkey=pubkey
@ -115,21 +115,21 @@ class KomradeAsymmetricPrivateKey(KomradeAsymmetricKey):
def data(self): return self.privkey def data(self): return self.privkey
@property @property
def key(self): return self.privkey def key(self): return self.privkey
def __repr__(self): return f'''[Asymmetric Private Key] ({self.discreet})''' def __repr__(self): return f'''[Asymmetric Private Key]\n ({self.discreet})'''
def make_key_discreet(data,chance_redacted=0.5): def make_key_discreet(data,chance_unredacted=0.333):
import random import random
if not data: return '?' if not data: return '?'
if not isBase64(data): data=b64encode(data) if not isBase64(data): data=b64encode(data)
key=data.decode() key=data.decode()
return ''.join((k if random.random()<chance_redacted else '-') for k in key) return ''.join((k if random.random()<chance_unredacted else '-') for k in key)
def make_key_discreet_str(string,chance_redacted=0.5): def make_key_discreet_str(string,chance_unredacted=0.5):
import random import random
if not string: return '?' if not string: return '?'
return ''.join((k if random.random()<chance_redacted else '-') for k in string) return ''.join((k if random.random()<chance_unredacted else '-') for k in string)
def make_key_discreet1(data,len_start=10,len_end=10,ellipsis='.',show_len=True): def make_key_discreet1(data,len_start=10,len_end=10,ellipsis='.',show_len=True):
@ -145,18 +145,18 @@ class KomradeEncryptedKey(Logger):
def __init__(self,data): self.data=data def __init__(self,data): self.data=data
@property @property
def data_b64(self): return b64encode(self.data).decode() def data_b64(self): return b64encode(self.data).decode()
def __repr__(self): return f'[Encrypted Key] ({self.discreet})' def __repr__(self): return f'[Encrypted Key]\n ({self.discreet})'
@property @property
def discreet(self): return make_key_discreet(self.data) def discreet(self): return make_key_discreet(self.data)
def __str__(self): def __str__(self):
return repr(self) return repr(self)
class KomradeEncryptedAsymmetricPrivateKey(KomradeEncryptedKey): class KomradeEncryptedAsymmetricPrivateKey(KomradeEncryptedKey):
def __repr__(self): return f'[Encrypted Asymmetric Private Key] ({self.discreet})' def __repr__(self): return f'[Encrypted Asymmetric Private Key]\n ({self.discreet})'
class KomradeEncryptedAsymmetricPublicKey(KomradeEncryptedKey): class KomradeEncryptedAsymmetricPublicKey(KomradeEncryptedKey):
def __repr__(self): return f'[Encrypted Asymmetric Public Key] ({self.discreet})' def __repr__(self): return f'[Encrypted Asymmetric Public Key]\n ({self.discreet})'
class KomradeEncryptedSymmetricKey(KomradeEncryptedKey): class KomradeEncryptedSymmetricKey(KomradeEncryptedKey):
def __repr__(self): return f'[Encrypted Symmetric Key] ({self.discreet})' def __repr__(self): return f'[Encrypted Symmetric Key]\n ({self.discreet})'
def get_encrypted_key_obj(data,name_of_encrypted_key): def get_encrypted_key_obj(data,name_of_encrypted_key):
if name_of_encrypted_key.startswith('privkey'): if name_of_encrypted_key.startswith('privkey'):

@ -9,7 +9,7 @@ class Persona(Caller):
super().__init__(name=name,passphrase=passphrase) super().__init__(name=name,passphrase=passphrase)
if SHOW_STATUS: if SHOW_STATUS:
from komrade.cli import CLI from komrade.cli import CLI
self.cli = CLI() self.cli = CLI(name=name, persona=self)
# self.boot(create=False) # self.boot(create=False)
# def boot(self,create=False): # def boot(self,create=False):
@ -54,18 +54,17 @@ class Persona(Caller):
# intro narration? # intro narration?
if SHOW_STATUS and show_intro: if SHOW_STATUS and show_intro:
name = self.cli.status_keymaker_intro(name) name = self.cli.status_keymaker_part1(name)
# 1) forge public/private keys # 1) forge public/private keys
keypair = KomradeAsymmetricKey() keypair = KomradeAsymmetricKey()
pubkey,privkey = keypair.pubkey_obj,keypair.privkey_obj pubkey,privkey = keypair.pubkey_obj,keypair.privkey_obj
self.log(f'Keymaker has cut private and public keys:\n\n(1) {pubkey}\n\n(2) {privkey}')
# 2) make sure we have passphrase # 2) make sure we have passphrase
passphrase=self.crypt_keys.hash(b'boogywoogy') # passphrase=self.crypt_keys.hash(b'boogywoogy')
if SHOW_STATUS and not passphrase: if SHOW_STATUS and not passphrase:
passphrase = self.cli.status_keymaker_body( passphrase = self.cli.status_keymaker_part2(
name, name,
passphrase, passphrase,
pubkey, pubkey,
@ -76,18 +75,38 @@ class Persona(Caller):
else: else:
if not passphrase: passphrase=getpass('Enter a memorable password to encrypt your private key with: ') if not passphrase: passphrase=getpass('Enter a memorable password to encrypt your private key with: ')
# 2) hash password
passhash = self.crypt_keys.hash(passphrase.encode())
# self.log(f'Hasher has scrambled inputted password using SHA-256 hashing algorithm (partly redacted):\n\n[Hashed Password] {make_key_discreet_str(passhash)}')
self.log(f'''Keymaker has created a symmetric encryption cell using the disguised password:
(2A) [Symmetric Encryption Key]
({make_key_discreet_str(passhash)})''')
# 3) form an encryption key # 3) form an encryption key
privkey_decr = KomradeSymmetricKeyWithPassphrase(passphrase) privkey_decr = KomradeSymmetricKeyWithPassphrase(passhash)
privkey_encr = privkey_decr.encrypt(privkey.data) privkey_encr = privkey_decr.encrypt(privkey.data)
privkey_encr_obj = KomradeEncryptedAsymmetricPrivateKey(privkey_encr)
self.log(f'''This pass-generated key has now transformed the private key (2) into the following encrypted form (redacted):
self.cli.status_keymaker_part3(privkey,privkey_decr,privkey_encr,passphrase) (2B) [Encrypted Private Key]
({make_key_discreet_str(privkey_encr_obj.data_b64)}''')
exit() if SHOW_STATUS:
self.cli.status_keymaker_part3(
privkey,
privkey_decr,
privkey_encr,
passphrase,
)
# save the ones we should on server # save the ones we should on server
data = { data = {
**{'name':name, 'passphrase':self.crypt_keys.hash(passphrase.encode()), ROUTE_KEYNAME:'register_new_user'}, 'name':name,
**keys_returned 'pubkey': pubkey.data,
ROUTE_KEYNAME:'register_new_user'
} }
self.log('sending to server:',dict_format(data,tab=2)) self.log('sending to server:',dict_format(data,tab=2))
# msg_to_op = self.compose_msg_to(data, self.op) # msg_to_op = self.compose_msg_to(data, self.op)
@ -118,7 +137,7 @@ def test_register():
botname=f'marx{str(num).zfill(3)}' botname=f'marx{str(num).zfill(3)}'
marxbot = Persona(botname) marxbot = Persona(botname)
# marxbot=Persona() # marxbot=Persona()
marxbot.register() marxbot.register(passphrase='communise')
if __name__=='__main__': if __name__=='__main__':
test_register() test_register()

@ -56,9 +56,9 @@ ART_ROTARY2="""
ART_KEY = """ ART_KEY = """
8 8 8 8 ,ooo. 8 8 8 8 ,ooo.
8a8 8a8 oP ?b 8a8 8a8 oP b
d888a888zzzzzzzzzzzzzzzzzzzz8 8b d888a888zzzzzzzzzzzzzzzzzzzz8 8b
`""^""' ?o___oP' `""^""' o___oP'
""" """
@ -273,7 +273,7 @@ ART_KEY_PAIR2B = """
__ __
/o \\_____ /o \\_____
\__/-="="` \__/-="="`
__ (1) public key __
/ o\\ / o\\
\_ / \_ /
<| <|
@ -282,6 +282,62 @@ ART_KEY_PAIR2B = """
""" """
ART_KEY_PAIR2BC = """
__
/o \\_____
\__/-="="`
__________ __ |
| / o\\ |
| \_ / |
| <| |
| <| |
| <| |
|__________|
"""
CUBE = """
+--------------+
/| /|
/ | / |
*--+-----------* |
| | | |
| | | |
| | | |
| +-----------+--+
| / | /
|/ |/
*--------------*
"""
CUBEKEY = """
+--------------+
/| /|
/ | / |
*--+-----------* |
| | / o\\ | |
| | \_ / | |
| | <| | |
| +----<|-----+--+
| / <| | /
|/ |/
*--------------*
"""
ART_KEY_PAIR2Bz = """
__
/o \\_____
\__/-="="`
__ (1) public key
/ o\\
\_ /
<|
<|
<| (2) private key
"""
ART_KEY_PAIR_SPLITTING1 = """ ART_KEY_PAIR_SPLITTING1 = """
_ _
@ -328,13 +384,52 @@ ART_KEY_PAIR_SPLITTING3 = """
(2A) (2) (2B) (2A) (2) (2B)
symmetric asymmetric encrypted form symmetric asymmetric encrypted form
encryption private of asymmetric encryption private of (2)
key, from key private key (2) key, from key
hashed hashed
passphrase 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)
"""
@ -436,12 +531,12 @@ ART_KEY_PAIR4 = """
__ __
/o \\_____ /o \\_____
\__/-="="` \__/-="="`
? _ _
?? \\ \\
? _ / _ /
? | |
? | |
? | (2B) privkey_encr | (2B) privkey_encr
(2A) privkey_decr (2A) privkey_decr
@ -452,12 +547,12 @@ ART_KEY_PAIR4Z = """
__ __
/o \\_____ /o \\_____
\__/-="="` \__/-="="`
? _ _
?? \\ \\
? _ / _ /
? | |
? | |
? | |
(2A) privkey_decr (2A) privkey_decr
@ -465,36 +560,36 @@ ART_KEY_PAIR4Z = """
ART_KEY_PAIR4D = """ ART_KEY_PAIR4D = """
? _ _
?? \\ \\
? _ / _ /
? | |
? | |
? | (2B) privkey_encr | (2B) privkey_encr
(2A) privkey_decr (2A) privkey_decr
""" """
ART_KEY_PAIR4C = """ ART_KEY_PAIR4C = """
? _ _
?? \\ \\
? _ / _ /
? | |
? | |
? | |
(2A) privkey_decr (2A) privkey_decr
""" """
ART_KEY_PAIR4B = """ ? _ ART_KEY_PAIR4B = """ _
?? \\ \\
? _ / _ /
? | |
? | |
? | |
""" """
@ -547,12 +642,12 @@ ART_KEY_PAIR4Z1 = """
/o \\_____ /o \\_____
\__/-="="` \__/-="="`
(1) public key (1) public key
? _ _
?? \\ \\
? _ / _ /
? | |
? | |
? | |
(2B) encrypted (2B) encrypted
form of (2) form of (2)
@ -563,12 +658,12 @@ ART_KEY_PAIR4ZZ = """
/o \\_____ /o \\_____
\__/-="="` \__/-="="`
? _ _
?? \\ \\
? _ / _ /
? | |
? | |
? | |
@ -581,12 +676,12 @@ ART_KEY_PAIR4Z2 = """
/o \\_____ /o \\_____
\__/-="="` \__/-="="`
(1) public key (1) public key
? ?
?? ?
? ? ?
? ?
? ?
? ?
@ -597,12 +692,12 @@ ART_KEY_PAIR4Z3 = """
__ __
/o \\_____ /o \\_____
\__/-="="` \__/-="="`
?? (1) public key (1) public key
? ?
? ?
??
??
??
@ -612,12 +707,12 @@ ART_KEY_PAIR4Z3 = """
__ __
/o \\_____ /o \\_____
\__/-="="` \__/-="="`
?? (1) public key (1) public key
? ??
? ?
??
?
??
@ -625,15 +720,15 @@ ART_KEY_PAIR4Z3 = """
ART_KEY_PAIR4Z42 = """ ART_KEY_PAIR4Z42 = """
?
? ??????
?? ? ? ??
??
? ??
? ?
??
?
??
@ -642,9 +737,9 @@ ART_KEY_PAIR4Z42 = """
ART_KEY_PAIR4Z4B = """ ART_KEY_PAIR4Z4B = """
??
?? ??????
??????????
__ __
/ o\\ / o\\
\_ / \_ /
@ -658,9 +753,9 @@ ART_KEY_PAIR4Z4B = """
ART_KEY_PAIR4Z4 = """ ART_KEY_PAIR4Z4 = """
??
?? ??????
??????????
__ __
/ o\\ / o\\
\_ / \_ /
@ -673,12 +768,12 @@ ART_KEY_PAIR4Z4 = """
""" """
ART_KEY_PAIR3BB = """ ? _ ART_KEY_PAIR3BB = """ _
?? \\ \\
? _ / _ /
? | |
? | |
? | |
(2A) (2B) encrypted (2A) (2B) encrypted
encryption form of (2) encryption form of (2)
key for (2B) key for (2B)
@ -822,7 +917,7 @@ __|__________________|
# code # code
### ###
from PIL import Image from PIL import Image
ASCII_CHARS = [ '#', '?', '%', '.', 'S', '+', '.', '*', ':', ',', '@'] ASCII_CHARS = [ '#', ' ', '%', '.', 'S', '+', '.', '*', ':', ',', '@']
def scale_image(image, new_width=100): def scale_image(image, new_width=100):
"""Resizes an image preserving the aspect ratio. """Resizes an image preserving the aspect ratio.

@ -0,0 +1,920 @@
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)

@ -13,9 +13,10 @@ class CLI(Logger):
'login':'log back in' 'login':'log back in'
} }
def __init__(self): def __init__(self,name='',cmd='',persona=None):
self.name='' self.name=name
self.cmd='' self.cmd=cmd
self.persona=persona
def run(self,inp='',name=''): def run(self,inp='',name=''):
self.name=name self.name=name
@ -89,7 +90,7 @@ class CLI(Logger):
None,{ART_OLDPHONE4},f'''@{name}: Hm, ok. Well, could you patch me through to the remote operator then?''', 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 forge your encryption keys first.''', 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'@{name}: Fine, but how do I do that?',
@ -106,20 +107,8 @@ class CLI(Logger):
f'@Keymaker: Of course, Komrade @{name}.', f'@Keymaker: Of course, Komrade @{name}.',
) )
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( self.status(
'I will forge for you two matching keys, part of an "asymmetric" pair.', 'I will cut for you two matching keys, part of an "asymmetric" pair.',
'Please, watch me work.', 'Please, watch me work.',
None,{tw.indent(ART_KEY,' '*5)+'\n'}, None,{tw.indent(ART_KEY,' '*5)+'\n'},
@ -133,58 +122,55 @@ class CLI(Logger):
None, None,
{ART_KEY_PAIR,True} {ART_KEY_PAIR,True}
) #,clear=False,indent=10,pause=False) ) #,clear=False,indent=10,pause=False)
self.status(
None,{ART_KEY_PAIR}, return name
'A matching set of keys have been generated.',
None,{ART_KEY_PAIR2A+'\n\nA matching set of keys have been generated.'},
'1) First, I have made a "public key" which you can share with anyone:',
f'{repr(pubkey)}', def status_keymaker_part2(self,name,passphrase,pubkey,privkey,hasher,persona):
'This key is a randomly-generated binary string, which acts as your "address" on Komrade.', from getpass import getpass
'By sharing this key with someone, you enable them to write you an encrypted message which only you can read.' # gen what we need
) uri_id = pubkey.data_b64
self.status( qr_str = get_qr_str(uri_id)
None,{ART_KEY_PAIR2A}, qr_path = os.path.join(PATH_QRCODES,name+'.png')
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 phone to phone:', # what are pub/priv?
{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}.)' # 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 # private keys
self.status(None, self.status(None,
{ART_KEY_PAIR2B}, {ART_KEY_PAIR2B},
'Second, I have forged a matching "private key":', 'Second, I have cut a matching "private key".',
f'{repr(privkey)}', "It's too dangerous to show in full, so here it is 66% redacted:",
'With it, you can decrypt any message sent to you via your public key.', f'(2) {make_key_discreet(privkey.data_b64,0.3)}',
'You you should never, ever give this key to anyone.', 'With it, you can decrypt and read any message sent to you via your public key.',
'In fact, this key is so dangerous that I will immediately destroy it by splitting it into two half-keys:' 'You can also encrypt and send messages to other people whose public keys you have.',
) )
# private keys
self.status(None, self.status(None,
{ART_KEY_PAIR31A}, {CUBEKEY},
{ART_KEY_PAIR3B+'\n',True}, 'So if someone were to steal your private key, they could read your mail and forge your signature.'
3,'Allow me to explain.', 'You you should never, ever give your private key to anyone.',
'(2A) is a separate encryption key generated by your password.', 'In fact, this key is so dangerous that we need to lock it away immediately.',
'(2B) is a version of (2) which has been encrypted by (2A).', "We'll even throw away the key we use to lock this private key with!",
"Because (2) will be destroyed, to rebuild it requires decrypting (2B) with (2A).", "How? By regenerating it each time from your password.",
)
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.",
)
self.status(
None,{ART_KEY_PAIR4ZZ},
'So choosing a password is an important thing!'
) )
if not passphrase: if not passphrase:
@ -293,37 +279,83 @@ class CLI(Logger):
return passphrase return passphrase
def status_keymaker_part3(self,privkey,privkey_decr,privkey_encr,passphrase): def status_keymaker_part3(self,privkey,privkey_decr,privkey_encr,passphrase):
# self.status( self.status(
# None,{tw.indent(ART_KEY,' '*5)+'\n',True}, None,{tw.indent(ART_KEY,' '*5)+'\n',True},
# # None,{ART_+'\n',True}, # None,{ART_+'\n',True},
# 'Now that we have a hashed passphrase, we can generate the (2A) encryption key.', 'Now that we have a hashed passphrase, we can generate the (2A) encryption key.',
# {ART_KEY_KEY2A,True,0.1}, {ART_KEY_KEY2A,True,0.1},
# '''This key (2A) is formed using Themis's high-level symmetric encryption key library, SecureCell using Seal mode.''', '''The key is formed using Themis's high-level symmetric encryption library: SecureCell, using Seal mode.''',
# '''It uses the AES-256 encryption algorithm, which was developed by the U.S. National Institute of Standards and Technology (NIST) in 2001.''' 'This key (2A) then uses the AES-256 encryption algorithm to encrypt the super-sensitive private key (2):'
# ) )
s1=self.print('Now that we have (2A), we can use it to encrypt the super-sensitive private key (2):',ret=True)
s2a = self.print(f"(2A) {make_key_discreet_str(passphrase)}",ret=True)
screen1 = f'''{s1}\n\n{ART_KEY_PAIR_SPLITTING1}\n{s2a}'''
s0=str.center('[Encryption Process]',CLI_WIDTH)
s1=s0 + '\n\n' + self.print('Now that we have (2A), we can use it to encrypt the super-sensitive private key (2):',ret=True)
s2a = self.print(f"(2A) {make_key_discreet_str(passphrase)}",ret=True)
s2 = self.print(f"(2) {make_key_discreet(privkey.data_b64)}",ret=True) s2 = self.print(f"(2) {make_key_discreet(privkey.data_b64)}",ret=True)
screen2 = f'''{s1}\n\n{ART_KEY_PAIR_SPLITTING2}\n{s2a}\n\n\n{s2}'''
repr_privkey = repr(privkey).replace('] ',']\n')
s2b = self.print(f"(2B) {make_key_discreet(b64encode(privkey_encr))}",ret=True) s2b = self.print(f"(2B) {make_key_discreet(b64encode(privkey_encr))}",ret=True)
screen3 = f'''{s1}\n\n{ART_KEY_PAIR_SPLITTING3}\n{s2a}\n\n\n{s2}\n\n\n{s2b}''' 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,
)
self.status(None,s1)
self.status(None,{screen1}) shdr=str.center('[Decryption Process]',CLI_WIDTH) + '\n\n' + self.print('Once we have (2B), we don\'t need (2A) or (2) anymore. We can regenerate them!',ret=True)
do_pause() from getpass import getpass
self.status(None,{screen2})
do_pause() passhash = None
self.status(None,{screen3})
do_pause()
while passhash!=passphrase:
res = self.status(
None,{shdr},False if passhash is None else True,
("pass",self.print(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,
)
@ -338,4 +370,47 @@ def run_cli():
if __name__=='__main__': if __name__=='__main__':
run_cli() run_cli()
# asyncio.run(test_async()) # 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.",
)
"""

@ -0,0 +1,415 @@
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.persona = Persona(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.print('Now that we have (2A), we can use it to encrypt the super-sensitive private key (2):',ret=True)
s2a = self.print(f"(2A) {make_key_discreet_str(passphrase)}",ret=True)
s2 = self.print(f"(2) {make_key_discreet(privkey.data_b64)}",ret=True)
s2b = self.print(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.print('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.print(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.",
)
"""

@ -0,0 +1,483 @@
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
import curses,time,random
CLI_NCOLS = 50
CLI_NROWS = 100
class CLI(Logger):
ROUTES = {
'help':'see help messages',
'register':'register new user',
'login':'log back in'
}
def __init__(self,name='',cmd='',persona=None,stdscr=None):
self.name=name
self.cmd=cmd
self.persona=persona
self.boot(stdscr=stdscr)
def boot(self,indent=5,stdscr=None):
if not stdscr:
self.stdscr = stdscr = curses.initscr()
else:
self.stdscr = stdscr
# curses.noecho()
# curses.cbreak()
# stdscr.keypad(True)
stdscr.clear()
stdscr.refresh()
curses.start_color()
curses.init_pair(1, curses.COLOR_CYAN, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)
self.win = curses.newwin(
CLI_NROWS,
CLI_NCOLS,
)
def shutdown(self):
# curses.nocbreak()
# stdscr.keypad(False)
# curses.echo()
exit('Goodbye')
def scan_print(self,string,start_x=0,start_y=0):
for li,ln in enumerate(string.split('\n')):
for xi,x in enumerate(ln):
pos_x = start_x + xi
pos_y = start_y + li
self.stdscr.addstr(pos_y,pos_x,x)
time.sleep(random.uniform(0,0.01))
self.stdscr.refresh()
def run(self,inp='',name=''):
self.name=name
clear_screen()
# self.boot()
self.logo()
# self.help()
inp = chr(self.stdscr.getch())
if inp: self.route(inp)
while True:
i = random.randint(0,CLI_NCOLS)
j = random.randint(0,CLI_NCOLS)
self.stdscr.addstr(i,j,'!')
try:
# inp=input(f'@{self.name if self.name else "?"}: ')
inp = k = chr(self.stdscr.getch())
except KeyboardInterrupt:
self.shutdown()
self.route(inp)
self.stdscr.refresh()
#await asyncio.sleep(0.5)
# @property
def logo(self):
import textwrap as tw
logo=art.text2art(CLI_TITLE,font=CLI_FONT)
self.scan_print(logo)
return logo
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 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.persona = Persona(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.print('Now that we have (2A), we can use it to encrypt the super-sensitive private key (2):',ret=True)
s2a = self.print(f"(2A) {make_key_discreet_str(passphrase)}",ret=True)
s2 = self.print(f"(2) {make_key_discreet(privkey.data_b64)}",ret=True)
s2b = self.print(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.print('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.print(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 main():
import curses
curses.wrapper(run_cli)
def run_cli_curses(stdscr):
cli = CLI(stdscr=stdscr)
cli.run('/register','elon') #'/register',name='elon')
if __name__=='__main__':
# run_cli()
# asyncio.run(test_async())
main()
"""
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.",
)
"""

@ -182,10 +182,11 @@ DEFAULT_USER_SETTINGS = {
} }
SHOW_LOG = 1 SHOW_LOG = 1
SHOW_STATUS = 1 SHOW_STATUS = 0
PAUSE_LOGGER = 1 PAUSE_LOGGER = 1
CLI_TITLE = 'KOMRADE' CLI_TITLE = 'KOMRADE'
CLI_FONT = 'clr5x6'#'colossal' CLI_FONT = 'clr5x6'#'colossal'
CLI_WIDTH = STATUS_LINE_WIDTH = 50 CLI_WIDTH = STATUS_LINE_WIDTH = 50

@ -44,11 +44,15 @@ def dict_format(d, tab=0):
s = ['{\n\n'] s = ['{\n\n']
for k,v in sorted(d.items()): for k,v in sorted(d.items()):
v=reppr(v)
#print(k,v,type(v))
if isinstance(v, dict): if isinstance(v, dict):
v = dict_format(v, tab+1) v = dict_format(v, tab+1)
else: else:
v = repr(v) v = repr(v)
# s.append('%s%r: %s (%s),\n' % (' '*tab, k, v, type(v).__name__)) # s.append('%s%r: %s (%s),\n' % (' '*tab, k, v, type(v).__name__))
s.append('%s%r: %s,\n\n' % (' '*tab, k, reppr(v))) s.append('%s%r: %s,\n\n' % (' '*tab, k, reppr(v)))
s.append('%s}' % (' '*(tab-2))) s.append('%s}' % (' '*(tab-2)))
@ -63,7 +67,7 @@ class Logger(object):
calframe = inspect.getouterframes(curframe, 2) calframe = inspect.getouterframes(curframe, 2)
mytype = type(self).__name__ mytype = type(self).__name__
caller = calframe[1][3] caller = calframe[1][3]
log(f'\n[{mytype}.{caller}()]',*x) log(f'[{mytype}.{caller}()]\n\n',*x)
# try: # try:
if pause: do_pause() if pause: do_pause()
@ -94,7 +98,6 @@ class Logger(object):
paras=[] paras=[]
res={} res={}
for para in msg: for para in msg:
indentstr=' '*indent
plen = para if type(para)==int or type(para)==float else None plen = para if type(para)==int or type(para)==float else None
if type(para) in {int,float}: if type(para) in {int,float}:
plen=int(para) plen=int(para)
@ -109,9 +112,9 @@ class Logger(object):
elif para is None: elif para is None:
clear_screen() clear_screen()
elif para is False: elif para is False:
pass do_pause()
elif para is True: elif para is True:
pass print()
elif type(para) is set: # logo/image elif type(para) is set: # logo/image
pl = [x for x in para if type(x)==str] pl = [x for x in para if type(x)==str]
txt=pl[0] txt=pl[0]
@ -131,8 +134,6 @@ class Logger(object):
res[k]=ans res[k]=ans
elif type(para) is dict: elif type(para) is dict:
print(dict_format(para,tab=tab)) print(dict_format(para,tab=tab))
elif para is 0:
do_pause()
elif pause: elif pause:
self.print(para,flush=True,end=end if end else '\n',scan=scan,indent=indent) self.print(para,flush=True,end=end if end else '\n',scan=scan,indent=indent)
paras+=[para] paras+=[para]
@ -261,7 +262,7 @@ def capture_stdout(func):
def scan_print(xstr,min_pause=0,max_pause=.001,speed=1): def scan_print(xstr,min_pause=0,max_pause=.01,speed=1):
import random,time import random,time
for c in xstr: for c in xstr:
print(c,end='',flush=True) print(c,end='',flush=True)

Loading…
Cancel
Save