added stream.py, still need func to add to stream

This commit is contained in:
Kevin Froman 2020-04-29 01:41:29 -05:00
parent cafa4e6d11
commit 0dd9c1e9ac
6 changed files with 106 additions and 31 deletions

View File

@ -54,3 +54,12 @@ That said, one should not rely on any software when the stakes are too high.
![](dummy.png) ![](dummy.png)
# Limitations + Road map
This project will forever follow the KISS principle, but these two three will be addressed.
* Multi-byte character support (full utf-8 support)
* Tor bridge support
* Support non-anonymous hidden services. Mainly useful for certain development needs

View File

@ -16,6 +16,7 @@ setup(name='youandme',
scripts=['src/yam/yam.py'], scripts=['src/yam/yam.py'],
author_email='beardog@mailbox.org', author_email='beardog@mailbox.org',
url='http://github.com/beardog108/youandme', url='http://github.com/beardog108/youandme',
python_requires='>=3.7',
install_requires=[ install_requires=[
'stem', 'stem',
'PySocks' 'PySocks'

View File

@ -6,6 +6,7 @@ from threading import Thread
import tempfile import tempfile
import argparse import argparse
from string import printable from string import printable
from base64 import b85decode, b85encode
import socks import socks
from stem.control import Controller from stem.control import Controller
@ -15,6 +16,7 @@ try:
except ModuleNotFoundError: except ModuleNotFoundError:
pass pass
from youandme.commands import terminator
from youandme.server import server from youandme.server import server
from youandme.client import client from youandme.client import client
@ -22,31 +24,37 @@ from youandme.client import client
class Connection: class Connection:
connected = True connected = True
class _Address: class _Address:
address = "" address = ""
def _get_open_port(): def _get_open_port():
# taken from (but modified) https://stackoverflow.com/a/2838309 by https://stackoverflow.com/users/133374/albert ccy-by-sa-3 https://creativecommons.org/licenses/by-sa/3.0/ # taken from (but modified) stackoverflow.com/a/2838309
# changes from source: import moved to top of file, bind specifically to localhost # by stackoverflow.com/users/133374/albert
# ccy-by-sa-3 creativecommons.org/licenses/by-sa/3.0/
# changes from source: import moved to top of file, bind to localhost
# vulnerable to race condition # vulnerable to race condition
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("127.0.0.1",0)) s.bind(("127.0.0.1", 0))
s.listen(1) s.listen(1)
port = s.getsockname()[1] port = s.getsockname()[1]
s.close() s.close()
return port return port
def connector(host, send_data, recv_data, address="", control_port=1337, socks_port=1338):
def connector(host, send_data, recv_data,
address="", control_port=1337, socks_port=1338):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = sock.connect_ex(('127.0.0.1', socks_port)) result = sock.connect_ex(('127.0.0.1', socks_port))
sock.close()
if result != 0: if result != 0:
try: try:
launch_tor(control_port=control_port, socks_port=socks_port) launch_tor(control_port=control_port, socks_port=socks_port)
except OSError: except OSError:
with tempfile.TemporaryDirectory() as tmpdirname: with tempfile.TemporaryDirectory() as tmpdirname:
launch_tor(control_port=control_port, socks_port=socks_port, data_dir=tmpdirname) launch_tor(control_port=control_port,
socks_port=socks_port, data_dir=tmpdirname)
if host: if host:
with Controller.from_port(port=control_port) as controller: with Controller.from_port(port=control_port) as controller:
controller.authenticate() controller.authenticate()
@ -62,7 +70,8 @@ def connector(host, send_data, recv_data, address="", control_port=1337, socks_p
) )
_Address.address = serv.service_id _Address.address = serv.service_id
conn, addr = s.accept() conn, addr = s.accept()
server(0.01, controller, conn, send_data, recv_data, Connection) server(0.01, controller, conn, send_data, recv_data,
Connection)
else: else:
if not address.endswith('.onion'): if not address.endswith('.onion'):
address += '.onion' address += '.onion'
@ -71,8 +80,10 @@ def connector(host, send_data, recv_data, address="", control_port=1337, socks_p
except socks.GeneralProxyError: except socks.GeneralProxyError:
Connection.connected = False Connection.connected = False
def chat(mode, send_data, recv_data, alpha): def chat(mode, send_data, recv_data, alpha):
print("A notice will be shown when connection is established, but messages may be typed now.") print("A notice will be shown when connection is established," +
"but messages may be typed now.")
display_buffer = [] display_buffer = []
if mode == 'host': if mode == 'host':
@ -83,27 +94,16 @@ def chat(mode, send_data, recv_data, alpha):
print(_Address.address) print(_Address.address)
def display_new(): def display_new():
while True: for message in get_messages
try:
char = chr(recv_data.pop(0))
display_buffer.append(char)
if char == "\n" or char == "\r\n" or len(display_buffer) > 100:
while len(display_buffer) != 0:
char = display_buffer.pop(0)
if alpha and char not in printable:
continue
print("\033[1;33m" + char + "\033[0m", end="")
except IndexError:
pass
sleep(0.1)
Thread(target=display_new, daemon=True).start() Thread(target=display_new, daemon=True).start()
def make_message(): def make_message():
while True: while True:
new = input("\033[0m").encode('utf-8') new = input("\033[0m").encode('utf-8') # nosec
for b in new: for b in new:
send_data.append(b) send_data.append(b)
send_data.append(ord(b"\n")) send_data.append(ord(b"\n"))
Thread(target=make_message, daemon=True).start() Thread(target=make_message, daemon=True).start()
while True: while True:
try: try:
@ -113,15 +113,25 @@ def chat(mode, send_data, recv_data, alpha):
sleep(1) sleep(1)
except KeyboardInterrupt: except KeyboardInterrupt:
break break
PORT_MESSAGE = "Specify any free ports above 1023" PORT_MESSAGE = "Specify any free ports above 1023"
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description='End-to-end encrypted instant messaging with metadata privacy and perfect forward secrecy') parser = argparse.ArgumentParser(
description='End-to-end encrypted messaging with metadata privacy')
parser.add_argument('connection', choices=['host', 'conn']) parser.add_argument('connection', choices=['host', 'conn'])
parser.add_argument('--socks-port', help='Socks proxy port (will use random if not given)', default=0, type=int) parser.add_argument('--socks-port',
parser.add_argument('--control-port', help='Tor control port (will use random if not given)', default=0, type=int) help='Socks proxy port (will use random if not given)',
parser.add_argument('--alphanum-only', help='Only allow english alpha-numeric characters and spaces/tabs/new lines', default=False, type=bool) default=0, type=int)
parser.add_argument('--address', help='Address to connect to. No port.', default='') parser.add_argument('--control-port',
help='Tor control port (will use random if not given)',
default=0, type=int)
parser.add_argument('--alphanum-only',
help='Only stdout en-us typical characters',
default=False, type=bool)
parser.add_argument('--address',
help='Address to connect to. No port.', default='')
args = parser.parse_args() args = parser.parse_args()
if args.socks_port == 0: args.socks_port = _get_open_port() if args.socks_port == 0: args.socks_port = _get_open_port()
@ -130,16 +140,17 @@ if __name__ == "__main__":
recv_data = bytearray() recv_data = bytearray()
if args.connection == 'conn': if args.connection == 'conn':
if not args.address: if not args.address:
print("Must specify address if connecting (--address)", file=sys.stderr) print("Must specify address if connecting (--address)",
file=sys.stderr)
sys.exit(3) sys.exit(3)
Thread(target=connector, Thread(target=connector,
args=[False, send_data, recv_data], args=[False, send_data, recv_data],
kwargs={'address': args.address, kwargs={'address': args.address,
'socks_port': args.socks_port, 'socks_port': args.socks_port,
'control_port': args.control_port}, daemon=True).start() 'control_port': args.control_port}, daemon=True).start()
chat('conn', send_data, recv_data, args.alphanum_only) chat('conn', send_data, recv_data, args.alphanum_only)
else: else:
Thread(target=connector, args=[True, send_data, recv_data], Thread(target=connector, args=[True, send_data, recv_data],
kwargs={'socks_port': args.socks_port, kwargs={'socks_port': args.socks_port,
'control_port': args.control_port}, daemon=True).start() 'control_port': args.control_port}, daemon=True).start()
chat('host', send_data, recv_data, args.alphanum_only) chat('host', send_data, recv_data, args.alphanum_only)

View File

@ -1,2 +1,3 @@
garbage_character = b'\x00' garbage_character = b'\x00'
terminator = 1
WELCOME_MESSAGE = "Connection established. Dance like no one is watching :)\n" WELCOME_MESSAGE = "Connection established. Dance like no one is watching :)\n"

19
src/youandme/stream.py Normal file
View File

@ -0,0 +1,19 @@
from base64 import b85encode, b85decode
from time import sleep
from youandme.commands import terminator
_b85alphabet = (b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
def decoded_recv_stream(raw_stream: bytearray, delay_seconds: int, max_buffer_size=) -> bytes:
while True:
for byte in raw_stream:
if byte == terminator:
yield b85decode(raw_stream[:len(raw_stream) - 1])
raw_stream.clear()
continue
if byte not in _b85alphabet:
raise ValueError('Not valid base85 encoding')
sleep(delay_seconds)

34
tests/test_stream.py Normal file
View File

@ -0,0 +1,34 @@
import unittest
import socket
from threading import Thread
import sys
import time
from base64 import b85decode, b85encode
from stem.control import Controller
from youandme import stream
from youandme.commands import terminator
def add_encoded_to_bytes_array_valid(message, array: bytearray):
for m in b85encode(message.encode('utf-8')):
array.append(m)
array.append(terminator)
class TestStream(unittest.TestCase):
def test_stream_get(self):
send_data = bytearray()
recv_data = bytearray()
data = "hello world"
#stream.decoded_recv_stream(recv_data, data)
add_encoded_to_bytes_array_valid(data, recv_data)
c = 0
for message in stream.decoded_recv_stream(recv_data, 1):
self.assertEqual(message.decode('utf-8'), data)
break
unittest.main()