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

@ -23,4 +23,3 @@ class Caller(Operator):
# @hack: repurposing this for now as a narrator
e

@ -117,19 +117,19 @@ class KomradeAsymmetricPrivateKey(KomradeAsymmetricKey):
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):
def make_key_discreet(data,chance_redacted=0.5):
import random
if not data: return '?'
if not isBase64(data): data=b64encode(data)
key=data.decode()
return ''.join((k if random.random()<chance_bowdlerize else '-') for k in key)
return ''.join((k if random.random()<chance_redacted else '-') for k in key)
def make_key_discreet_str(string,chance_bowdlerize=0.5):
def make_key_discreet_str(string,chance_redacted=0.5):
import random
if not string: return '?'
return ''.join((k if random.random()<chance_bowdlerize else '-') for k in string)
return ''.join((k if random.random()<chance_redacted else '-') for k in string)
def make_key_discreet1(data,len_start=10,len_end=10,ellipsis='.',show_len=True):

@ -58,25 +58,31 @@ class Persona(Caller):
# forge public/private keys
# 1) forge public/private keys
keypair = KomradeAsymmetricKey()
pubkey,privkey = keypair.pubkey_obj,keypair.privkey_obj
# make sure we have passphrase
if SHOW_STATUS:
# 2) make sure we have passphrase
passphrase=self.crypt_keys.hash(b'boogywoogy')
if SHOW_STATUS and not passphrase:
passphrase = self.cli.status_keymaker_body(
name,
passphrase,
pubkey,
privkey,
self.crypt_keys.hash
self.crypt_keys.hash,
self
)
else:
if not passphrase: passphrase=getpass('Enter a memorable password to encrypt your private key with: ')
# encrypt private key
exit()
# 3) form an encryption key
privkey_decr = KomradeSymmetricKeyWithPassphrase(passphrase)
privkey_encr = privkey_decr.encrypt(privkey.data)
self.cli.status_keymaker_part3(privkey,privkey_decr,privkey_encr,passphrase)
exit()
# save the ones we should on server
data = {

@ -282,6 +282,59 @@ ART_KEY_PAIR2B = """
"""
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 asymmetric
key, from key private key (2)
hashed
passphrase
"""
@ -319,6 +372,14 @@ ART_KEY_PAIR3A2 = """
"""
ART_KEY_KEY2A = """ _
/o
\
<
<
<
"""
ART_KEY_PAIR3B2 = """
@ -496,6 +557,24 @@ ART_KEY_PAIR4Z1 = """
form of (2)
"""
ART_KEY_PAIR4ZZ = """
__
/o \\_____
\__/-="="`
? _
?? \\
? _ /
? |
? |
? |
"""
ART_KEY_PAIR4Z2 = """
__

@ -20,8 +20,8 @@ class CLI(Logger):
def run(self,inp='',name=''):
self.name=name
clear_screen()
self.boot()
self.help()
# self.boot()
# self.help()
if inp: self.route(inp)
@ -45,7 +45,7 @@ class CLI(Logger):
def boot(self,indent=5):
logo=art.text2art(CLI_TITLE,font=CLI_FONT)
# logo=make_key_discreet_str(logo,chance_bowdlerize=0.1) #.decode()
# logo=make_key_discreet_str(logo,chance_redacted=0.1) #.decode()
logo=tw.indent(logo, ' '*indent)
scan_print(logo,max_pause=0.005)
@ -69,7 +69,7 @@ class CLI(Logger):
### DIALOGUES
# hello, op?
def status_keymaker_intro(self,name):
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 '?'
@ -110,131 +110,216 @@ class CLI(Logger):
def status_keymaker_body(self,name,passphrase,pubkey,privkey,hasher):
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(
# 'I will forge for you two matching keys, part of an "asymmetric" pair.',
# 'Please, watch me work.',
# what are pub/priv?
self.status(
'I will forge for you two matching keys, part of an "asymmetric" pair.',
'Please, watch me work.',
# None,{tw.indent(ART_KEY,' '*5)+'\n'},
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)
# self.status(
# None,{ART_KEY_PAIR},
# '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)}',
# 'This key is a randomly-generated binary string, which acts as your "address" on Komrade.',
# 'By sharing this key with someone, you enable them to 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 phone to phone:',
# {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}.)'
# )
'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)
self.status(
None,{ART_KEY_PAIR},
'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)}',
'This key is a randomly-generated binary string, which acts as your "address" on Komrade.',
'By sharing this key with someone, you enable them to 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 phone to phone:',
{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 forged a matching "private key":',
# f'{repr(privkey)}',
# 'With it, you can decrypt any message sent to you via your public key.',
# 'You you should never, ever give this key to anyone.',
# 'In fact, this key is so dangerous that I will immediately destroy it by splitting it into two half-keys:'
# )
# 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.",
self.status(None,
{ART_KEY_PAIR2B},
'Second, I have forged a matching "private key":',
f'{repr(privkey)}',
'With it, you can decrypt any message sent to you via your public key.',
'You you should never, ever give this key to anyone.',
'In fact, this key is so dangerous that I will immediately destroy it by splitting it into two half-keys:'
)
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.",
)
self.status(
None,{ART_KEY_PAIR4Z1},
None,{ART_KEY_PAIR4ZZ},
'So choosing a password is an important thing!'
)
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,
{ART_FROG_BLENDER,True},
{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_str = hasher(str_to_hash.encode())
hashed_str1 = hasher(str_to_hash.encode())
res = self.status(
'@Hasher: '+hashed_str
'@Hasher: '+hashed_str1
)
res = self.status(
'@Keymaker: See? Ok, now type in a password.'
('str_to_hash',f'@{name}: ',getpass)
'@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_pass1 = hasher(str_to_hash.encode())
hashed_str2 = hasher(str_to_hash.encode())
res = self.status(
'@Hasher: '+hashed_pass1
'@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(
'@Keymaker: Whatever you entered, it\'s already forgotten. That hashed mess is all that remains.',
'Now type in the same password one more time to verify it:',
('str_to_hash',f'@{name}: ',getpass)
('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_pass2 = hasher(str_to_hash.encode())
hashed_str3 = hasher(str_to_hash.encode())
res = self.status(
'@Hasher: '+hashed_pass2
'@Hasher: '+hashed_str3
)
if hashed_pass1==hashed_pass2:
self.status('The passwords matched.')
if hashed_str2==hashed_str3:
self.status('See how the hashed values are also the same?')
else:
self.status('The passwords did not match.')
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},
# '''This key (2A) is formed using Themis's high-level symmetric encryption key 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.'''
# )
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}'''
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)
screen3 = f'''{s1}\n\n{ART_KEY_PAIR_SPLITTING3}\n{s2a}\n\n\n{s2}\n\n\n{s2b}'''
self.status(None,s1)
self.status(None,{screen1})
do_pause()
self.status(None,{screen2})
do_pause()
self.status(None,{screen3})
do_pause()

@ -188,4 +188,4 @@ PAUSE_LOGGER = 1
CLI_TITLE = 'KOMRADE'
CLI_FONT = 'clr5x6'#'colossal'
STATUS_LINE_WIDTH = 50
CLI_WIDTH = STATUS_LINE_WIDTH = 50

@ -71,7 +71,7 @@ class Logger(object):
# except KeyboardInterrupt:
# exit()
def print(*x,width=STATUS_LINE_WIDTH,end='\n',indent=1,scan=False,**y):
def print(*x,width=STATUS_LINE_WIDTH,end='\n',indent=1,ret=False,scan=False,**y):
if not scan and not width:
print(*x,end=end,**y)
else:
@ -82,6 +82,7 @@ class Logger(object):
# xw = [_ for _ in tw.wrap(xs,width=width)]
xs=end.join(xw)
xs = tw.indent(xs,' '*indent)
if ret: return xs
print(xs) if scan==False else scan_print(xs)
def status(self,*msg,pause=True,clear=False,ticks=[],tab=2,speed=10,end=None,indent=0,width=80,scan=False):
@ -114,7 +115,7 @@ class Logger(object):
elif type(para) is set: # logo/image
pl = [x for x in para if type(x)==str]
txt=pl[0]
speed =[x for x in para if type(x)==int]
speed =[x for x in para if type(x) in {int,float}]
speed = speed[0] if speed else 1
if True in para:
scan_print(txt,speed=speed)
@ -260,13 +261,13 @@ def capture_stdout(func):
def scan_print(xstr,min_pause=0,max_pause=0.015,speed=100000):
def scan_print(xstr,min_pause=0,max_pause=.001,speed=1):
import random,time
for c in xstr:
print(c,end='',flush=True)
# naptime=random.uniform(min_pause, max_pause / speed)
# time.sleep(naptime)
time.sleep(.001)
naptime=random.uniform(min_pause, max_pause / speed)
time.sleep(naptime)
# time.sleep()
@ -276,4 +277,10 @@ def get_qr_str(data):
qr.add_data(data)
ascii = capture_stdout(qr.print_ascii)
ascii = ascii[:-1] # removing last line break
return '\n ' + ascii.strip()
return '\n ' + ascii.strip()
def indent_str(x,n):
import textwrap as tw
return tw.indent(x,' '*n)
Loading…
Cancel
Save