updates
parent
832c82953e
commit
72d6821556
@ -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)
|
@ -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.",
|
||||
|
||||
)
|
||||
"""
|
Loading…
Reference in New Issue