initial commit

This commit is contained in:
nihonium 2023-01-15 13:53:21 +03:00
commit 3b3c9a9417
Signed by: nihonium
GPG key ID: 0251623741027CFC
258 changed files with 20086 additions and 0 deletions

View 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

View 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()

View 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

View file

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGySmc8D9mKY0VGif5el/bnbIlQeR
hLEWYtvvINH/IM5W2BfgtrrZVl5dyGy7tAWqpcqluIipcmcYcHqJndIneg==
-----END PUBLIC KEY-----

View file

@ -0,0 +1,2 @@
PyJWT
cryptography==3.3.2

View 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()