initial commit
This commit is contained in:
commit
3b3c9a9417
258 changed files with 20086 additions and 0 deletions
9
services/aesthetic/build/Dockerfile
Normal file
9
services/aesthetic/build/Dockerfile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
FROM python:3.6-alpine
|
||||
WORKDIR /dist
|
||||
RUN apk add --update build-base libffi-dev openssl-dev
|
||||
COPY src/requirements.txt /dist/requirements.txt
|
||||
RUN pip install --no-cache-dir -r requirements.txt && mkdir db
|
||||
COPY src .
|
||||
|
||||
CMD ["python", "-u", "app.py"]
|
||||
EXPOSE 8777
|
||||
113
services/aesthetic/build/src/app.py
Normal file
113
services/aesthetic/build/src/app.py
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import jwt
|
||||
import socket
|
||||
import socketserver
|
||||
from cipher import *
|
||||
from utils import *
|
||||
|
||||
|
||||
class SignatureFailure(Exception):
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Service connection handler
|
||||
"""
|
||||
|
||||
|
||||
class ForkingTCPServer(socketserver.ForkingTCPServer):
|
||||
def server_bind(self):
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
socketserver.TCPServer.server_bind(self)
|
||||
|
||||
|
||||
class ServiceServerHandler(socketserver.BaseRequestHandler):
|
||||
def __init__(self, request, client_address, server):
|
||||
socketserver.BaseRequestHandler.__init__(self, request, client_address, server)
|
||||
|
||||
def handle(self):
|
||||
logger.info('[%s] Accepted connection', self.client_address[0])
|
||||
try:
|
||||
# handle commands
|
||||
while True:
|
||||
cmd = read_message(self.request)
|
||||
logger.info('[%s] Accepted command: %s', self.client_address[0], cmd)
|
||||
|
||||
if cmd == b'PUSH':
|
||||
logger.info('[%s] PUSH: Executing', self.client_address[0])
|
||||
|
||||
capsule = read_message(self.request)
|
||||
game_round = read_message(self.request)
|
||||
iv = read_message(self.request)
|
||||
|
||||
encrypted_capsule, id = encrypt(capsule, iv)
|
||||
|
||||
send_message(self.request, encrypted_capsule)
|
||||
send_message(self.request, id)
|
||||
|
||||
signature = read_message(self.request)
|
||||
|
||||
with open('ec_public.pem', 'rb') as jwtkey:
|
||||
key = jwtkey.read()
|
||||
|
||||
if jwt.decode(signature, key, algorithms='ES256')['message'] != 'It\'s me, Mario!':
|
||||
send_message(self.request, b'-')
|
||||
raise SignatureFailure
|
||||
else:
|
||||
save_data('db/storage.db', str(game_round), encrypted_capsule, id)
|
||||
send_message(self.request, b'+')
|
||||
|
||||
elif cmd == b'PULL':
|
||||
logger.info('[%s] PULL: Executing', self.client_address[0])
|
||||
|
||||
# !!!
|
||||
game_round = read_message(self.request)
|
||||
|
||||
try:
|
||||
encrypted_capsule, retrieved_id = retrieve_data('db/storage.db', str(game_round))
|
||||
|
||||
send_message(self.request, encrypted_capsule)
|
||||
# !!!
|
||||
if read_message(self.request) == b'+':
|
||||
iv = read_message(self.request)
|
||||
id = read_message(self.request)
|
||||
|
||||
if id == retrieved_id:
|
||||
decrypted_capsule = decrypt(encrypted_capsule, iv)
|
||||
send_message(self.request, decrypted_capsule)
|
||||
else:
|
||||
send_message(self.request, b'-')
|
||||
except Exception as ex:
|
||||
send_message(self.request, b'-')
|
||||
logger.error('[%s] PULL: Exception: %s', self.client_address[0], ex)
|
||||
|
||||
elif cmd == b'EXIT':
|
||||
send_message(self.request, b'+')
|
||||
break
|
||||
|
||||
else:
|
||||
raise Exception('[%s] Failed to process command: command %s is unknown', self.client_address[0], cmd)
|
||||
|
||||
except Exception as ex:
|
||||
logger.error(str(ex), exc_info=True)
|
||||
|
||||
finally:
|
||||
logger.info('[%s] Processed connection', self.client_address[0])
|
||||
|
||||
|
||||
"""
|
||||
main
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
# initialize logging
|
||||
logging.basicConfig(format='%(asctime)s [%(levelname)-5.5s] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S',
|
||||
level=logging.DEBUG)
|
||||
logger = logging.getLogger('service')
|
||||
|
||||
# initialize and spawn the server
|
||||
create_database('db/storage.db')
|
||||
server = ForkingTCPServer(('0.0.0.0', 8777), ServiceServerHandler)
|
||||
server.serve_forever()
|
||||
118
services/aesthetic/build/src/cipher.py
Normal file
118
services/aesthetic/build/src/cipher.py
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.ciphers import (
|
||||
Cipher, algorithms, modes
|
||||
)
|
||||
|
||||
KEY = b'\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08'
|
||||
|
||||
|
||||
# region Utils
|
||||
|
||||
def gmul(x: int, y: int):
|
||||
r = int('0b11100001' + '0' * 120, 2)
|
||||
z = 0
|
||||
v = x
|
||||
y_bit = bin(y)[2:]
|
||||
for i in range(128):
|
||||
if y_bit[i] == '1':
|
||||
z ^= v
|
||||
if (v & 1) == 0:
|
||||
v >>= 1
|
||||
else:
|
||||
v = (v >> 1) ^ r
|
||||
|
||||
return z
|
||||
|
||||
# endregion Utils
|
||||
|
||||
|
||||
# region The Cipher
|
||||
|
||||
def encrypt_block(block):
|
||||
encryptor = Cipher(
|
||||
algorithms.AES(KEY),
|
||||
modes.ECB(),
|
||||
backend=default_backend()
|
||||
).encryptor()
|
||||
|
||||
ciphertext = encryptor.update(block) + encryptor.finalize()
|
||||
|
||||
return ciphertext
|
||||
|
||||
|
||||
def encrypt(plaintext: bytes, iv: bytes):
|
||||
ciphertext = []
|
||||
salt = encrypt_block(b'\x00' * 16)
|
||||
|
||||
blocks = []
|
||||
for i in range(0, len(plaintext), 16):
|
||||
blocks.append(plaintext[i:i+16])
|
||||
|
||||
j = [iv[:12] + b'\x00' * 3 + b'\x01', iv[:12] + b'\x00' * 3 + b'\x02']
|
||||
j_int = int.from_bytes(j[1], 'big')
|
||||
cnt = 0
|
||||
|
||||
while cnt < len(blocks):
|
||||
j_bytes = j_int.to_bytes(16, 'big')
|
||||
enc_j = int.from_bytes(encrypt_block(j_bytes), 'big')
|
||||
enc_j_bytes = enc_j.to_bytes(16, 'big')[:len(blocks[cnt])]
|
||||
enc_j = int.from_bytes(enc_j_bytes, 'big')
|
||||
block_i = int.from_bytes(blocks[cnt], 'big')
|
||||
ctext_i = enc_j ^ block_i
|
||||
ciphertext.append(ctext_i.to_bytes(len(blocks[cnt]), 'big'))
|
||||
|
||||
j_int = (j_int + 1) % (2**128)
|
||||
|
||||
cnt += 1
|
||||
|
||||
id = 0
|
||||
for ctext_i in ciphertext:
|
||||
if len(ctext_i) != 16:
|
||||
ctext_j = ctext_i + b'\x00' * (16 - len(ctext_i) % 16)
|
||||
else:
|
||||
ctext_j = ctext_i
|
||||
id = gmul((id ^ int.from_bytes(ctext_j, 'big')), int.from_bytes(salt, 'big'))
|
||||
|
||||
l = len(plaintext) * 8
|
||||
|
||||
id = gmul(id ^ l, int.from_bytes(salt, 'big'))
|
||||
id = id ^ int.from_bytes(encrypt_block(j[0]), 'big')
|
||||
id = id.to_bytes(16, 'big')
|
||||
|
||||
print(encrypt_block(j[0]))
|
||||
|
||||
return b''.join(ciphertext), id
|
||||
|
||||
|
||||
def decrypt(ciphertext: bytes, iv: bytes):
|
||||
plaintext = []
|
||||
|
||||
blocks = []
|
||||
for i in range(0, len(ciphertext), 16):
|
||||
blocks.append(ciphertext[i:i+16])
|
||||
|
||||
j = [iv[:12] + b'\x00' * 3 + b'\x01', iv[:12] + b'\x00' * 3 + b'\x02']
|
||||
j_int = int.from_bytes(j[1], 'big')
|
||||
cnt = 0
|
||||
|
||||
while cnt < len(blocks):
|
||||
j_bytes = j_int.to_bytes(16, 'big')
|
||||
enc_j = int.from_bytes(encrypt_block(j_bytes), 'big')
|
||||
enc_j_bytes = enc_j.to_bytes(16, 'big')[:len(blocks[cnt])]
|
||||
enc_j = int.from_bytes(enc_j_bytes, 'big')
|
||||
block_i = int.from_bytes(blocks[cnt], 'big')
|
||||
ptext_i = enc_j ^ block_i
|
||||
byte_length = ptext_i.bit_length() // 8
|
||||
if ptext_i.bit_length() % 8 != 0:
|
||||
byte_length += 1
|
||||
plaintext.append(ptext_i.to_bytes(byte_length, 'big'))
|
||||
|
||||
j_int = (j_int + 1) % (2**128)
|
||||
|
||||
cnt += 1
|
||||
|
||||
return b''.join(plaintext)
|
||||
|
||||
# endregion The Cipher
|
||||
4
services/aesthetic/build/src/ec_public.pem
Normal file
4
services/aesthetic/build/src/ec_public.pem
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGySmc8D9mKY0VGif5el/bnbIlQeR
|
||||
hLEWYtvvINH/IM5W2BfgtrrZVl5dyGy7tAWqpcqluIipcmcYcHqJndIneg==
|
||||
-----END PUBLIC KEY-----
|
||||
2
services/aesthetic/build/src/requirements.txt
Normal file
2
services/aesthetic/build/src/requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
PyJWT
|
||||
cryptography==3.3.2
|
||||
83
services/aesthetic/build/src/utils.py
Normal file
83
services/aesthetic/build/src/utils.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import struct
|
||||
import sqlite3
|
||||
|
||||
|
||||
logger = logging.getLogger('service')
|
||||
|
||||
|
||||
"""
|
||||
Communication
|
||||
"""
|
||||
|
||||
class InputOverflowException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InputUnderflowException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def read_message(s, max_input_length=1024*16) -> bytes:
|
||||
received_buffer = s.recv(8)
|
||||
logger.info('[%s] Accepted connection', self.client_address[0])
|
||||
if len(received_buffer) < 8:
|
||||
raise InputUnderflowException('Failed to receive data: the received length is less than 8 bytes long')
|
||||
to_receive = struct.unpack('<Q', received_buffer[0:8])[0]
|
||||
if to_receive > max_input_length:
|
||||
raise InputOverflowException('Failed to receive data: requested to accept too much data')
|
||||
received_buffer = b''
|
||||
|
||||
while len(received_buffer) < to_receive:
|
||||
data = s.recv(to_receive - len(received_buffer))
|
||||
if len(data) == 0:
|
||||
raise InputUnderflowException('Failed to receive data: the pipe must have been broken')
|
||||
received_buffer += data
|
||||
if len(received_buffer) > max_input_length:
|
||||
raise InputOverflowException('Failed to receive data: accepted too much data')
|
||||
|
||||
return received_buffer
|
||||
|
||||
|
||||
def send_message(s, message: bytes):
|
||||
send_buffer = struct.pack('<Q', len(message)) + message
|
||||
s.sendall(send_buffer)
|
||||
|
||||
|
||||
"""
|
||||
Database
|
||||
"""
|
||||
|
||||
|
||||
def create_database(db_file_path: str):
|
||||
conn = sqlite3.connect(db_file_path)
|
||||
with conn:
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('CREATE TABLE data (gr TEXT UNIQUE, c TEXT, id TEXT)')
|
||||
except sqlite3.OperationalError as ex:
|
||||
logger.error('Failed to create table: %s', ex)
|
||||
conn.close()
|
||||
|
||||
|
||||
def save_data(db_file_path: str, gr: str, c: bytes, id: bytes):
|
||||
conn = sqlite3.connect(db_file_path)
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('INSERT INTO data VALUES (?,?,?)', (gr, c, id))
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def retrieve_data(db_file_path: str, gr: str) -> tuple:
|
||||
conn = sqlite3.connect(db_file_path)
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SELECT * FROM data WHERE gr=?', (gr, ))
|
||||
row = cursor.fetchone()
|
||||
_, c, id = row
|
||||
return c, id
|
||||
finally:
|
||||
conn.close()
|
||||
Loading…
Add table
Add a link
Reference in a new issue