def __init__(self, config=None, status=None): if config is None: config = {} self.config = config if 'chunksize' not in config: self.config['chunksize'] = 1024 * 1024 * 8 # 8MB crypto.init(config) compression.init(config) self.status = status self.blockmap = {} self.inittime = int(time()) self.oldfiles = {} self.transport = MetaTransport(config, status) self.root = '/' self.force = False
def includeme(config): # authorization #config.include('pyramid_ipauth') config.include("cornice") config.scan("signing.views") crypto.init(key=config.registry.settings['signing.keyfile'], cert=config.registry.settings['signing.certfile']) issuers = config.registry.settings.get('signing.permitted_issuers', '') issuers = issuers.split(',') iss = [] for issuer in issuers: iss.append(issuer.strip()) if len(iss) < 1: raise Exception("No issuers provided in the config file!") config.registry.settings['signing.permitted_issuers'] = iss
def encode(spec, encrypt=True): """Encode a pydict specification into a OpenThings binary payload""" # The message is not encrypted, but the CRC is generated here. payload = [] # HEADER payload.append(0) # length, fixup later when known header = spec["header"] payload.append(header["mfrid"]) payload.append(header["productid"]) if not ("encryptPIP" in header): if encrypt: warning("no encryptPIP in header, assuming 0x0100") encryptPIP = 0x0100 else: encryptPIP = header["encryptPIP"] payload.append((encryptPIP&0xFF00)>>8) # MSB payload.append((encryptPIP&0xFF)) # LSB sensorId = header["sensorid"] payload.append((sensorId>>16) & 0xFF) # HIGH payload.append((sensorId>>8) & 0xFF) # MID payload.append((sensorId) & 0XFF) # LOW # RECORDS for rec in spec["recs"]: wr = rec["wr"] paramid = rec["paramid"] typeid = rec["typeid"] if "length" in rec: length = rec["length"] else: length = None # auto detect # PARAMID if wr: payload.append(0x80 + paramid) # WRITE else: payload.append(paramid) # READ # TYPE/LENGTH payload.append((typeid)) # need to back patch length for auto detect lenpos = len(payload)-1 # for backpatch # VALUE valueenc = [] # in case of no value if "value" in rec: value = rec["value"] valueenc = Value.encode(value, typeid, length) if len(valueenc) > 15: raise ValueError("value longer than 15 bytes") for b in valueenc: payload.append(b) payload[lenpos] = (typeid) | len(valueenc) # FOOTER payload.append(0) # NUL crc = calcCRC(payload, 5, len(payload)-5) payload.append((crc>>8) & 0xFF) # MSB payload.append(crc&0xFF) # LSB # back-patch the length byte so it is correct payload[0] = len(payload)-1 if encrypt: # ENCRYPT # [0]len,mfrid,productid,pipH,pipL,[5] crypto.init(crypt_pid, encryptPIP) crypto.cryptPayload(payload, 5, len(payload)-5) # including CRC return payload
def decode(payload, decrypt=True, receive_timestamp=None): """Decode a raw buffer into an OpenThings pydict""" #Note, decrypt must already have run on this for it to work length = payload[0] # CHECK LENGTH if length+1 != len(payload) or length < 10: raise OpenThingsException("bad payload length") ##return { ## "type": "BADLEN", ## "len_actual": len(payload), ## "len_expected": length, ## "payload": payload[1:] ##} # DECODE HEADER mfrId = payload[1] productId = payload[2] encryptPIP = (payload[3]<<8) + payload[4] header = { "mfrid" : mfrId, "productid" : productId, "encryptPIP": encryptPIP } if decrypt: # DECRYPT PAYLOAD # [0]len,mfrid,productid,pipH,pipL,[5] crypto.init(crypt_pid, encryptPIP) crypto.cryptPayload(payload, 5, len(payload)-5) # including CRC ##printhex(payload) # sensorId is in encrypted region sensorId = (payload[5]<<16) + (payload[6]<<8) + payload[7] header["sensorid"] = sensorId # CHECK CRC crc_actual = (payload[-2]<<8) + payload[-1] crc_expected = calcCRC(payload, 5, len(payload)-(5+2)) ##trace("crc actual:%s, expected:%s" %(hex(crc_actual), hex(crc_expected))) if crc_actual != crc_expected: raise OpenThingsException("bad CRC") ##return { ## "type": "BADCRC", ## "crc_actual": crc_actual, ## "crc_expected": crc_expected, ## "payload": payload[1:], ##} # DECODE RECORDS i = 8 recs = [] while i < length and payload[i] != 0: # PARAM param = payload[i] wr = ((param & 0x80) == 0x80) paramid = param & 0x7F if paramid in param_info: paramname = (param_info[paramid])["n"] # name paramunit = (param_info[paramid])["u"] # unit else: paramname = "UNKNOWN_" + hex(paramid) paramunit = "UNKNOWN_UNIT" i += 1 # TYPE/LEN typeid = payload[i] & 0xF0 plen = payload[i] & 0x0F i += 1 rec = { "wr": wr, "paramid": paramid, "paramname": paramname, "paramunit": paramunit, "typeid": typeid, "length": plen } if plen != 0: # VALUE valuebytes = [] for x in range(plen): valuebytes.append(payload[i]) i += 1 value = Value.decode(valuebytes, typeid, plen) rec["valuebytes"] = valuebytes rec["value"] = value # store rec recs.append(rec) m = { "type": "OK", "header": header, "recs": recs } if receive_timestamp != None: m["rxtimestamp"] = receive_timestamp return Message(m)
def encode(spec, encrypt=True): """Encode a pydict specification into a OpenThings binary payload""" # The message is not encrypted, but the CRC is generated here. payload = [] # HEADER payload.append(0) # length, fixup later when known header = spec["header"] payload.append(header["mfrid"]) payload.append(header["productid"]) if not ("encryptPIP" in header): if encrypt: warning("no encryptPIP in header, assuming 0x0100") encryptPIP = 0x0100 else: encryptPIP = header["encryptPIP"] payload.append((encryptPIP & 0xFF00) >> 8) # MSB payload.append((encryptPIP & 0xFF)) # LSB sensorId = header["sensorid"] payload.append((sensorId >> 16) & 0xFF) # HIGH payload.append((sensorId >> 8) & 0xFF) # MID payload.append((sensorId) & 0XFF) # LOW # RECORDS for rec in spec["recs"]: wr = rec["wr"] paramid = rec["paramid"] typeid = rec["typeid"] if "length" in rec: length = rec["length"] else: length = None # auto detect # PARAMID if wr: payload.append(0x80 + paramid) # WRITE else: payload.append(paramid) # READ # TYPE/LENGTH payload.append((typeid)) # need to back patch length for auto detect lenpos = len(payload) - 1 # for backpatch # VALUE valueenc = [] # in case of no value if "value" in rec: value = rec["value"] valueenc = Value.encode(value, typeid, length) if len(valueenc) > 15: raise ValueError("value longer than 15 bytes") for b in valueenc: payload.append(b) payload[lenpos] = (typeid) | len(valueenc) # FOOTER payload.append(0) # NUL crc = calcCRC(payload, 5, len(payload) - 5) payload.append((crc >> 8) & 0xFF) # MSB payload.append(crc & 0xFF) # LSB # back-patch the length byte so it is correct payload[0] = len(payload) - 1 if encrypt: # ENCRYPT # [0]len,mfrid,productid,pipH,pipL,[5] crypto.init(crypt_pid, encryptPIP) crypto.cryptPayload(payload, 5, len(payload) - 5) # including CRC return payload
def decode(payload, decrypt=True, receive_timestamp=None): """Decode a raw buffer into an OpenThings pydict""" #Note, decrypt must already have run on this for it to work length = payload[0] # CHECK LENGTH if length + 1 != len(payload) or length < 10: raise OpenThingsException("bad payload length") ##return { ## "type": "BADLEN", ## "len_actual": len(payload), ## "len_expected": length, ## "payload": payload[1:] ##} # DECODE HEADER mfrId = payload[1] productId = payload[2] encryptPIP = (payload[3] << 8) + payload[4] header = {"mfrid": mfrId, "productid": productId, "encryptPIP": encryptPIP} if decrypt: # DECRYPT PAYLOAD # [0]len,mfrid,productid,pipH,pipL,[5] crypto.init(crypt_pid, encryptPIP) crypto.cryptPayload(payload, 5, len(payload) - 5) # including CRC ##printhex(payload) # sensorId is in encrypted region sensorId = (payload[5] << 16) + (payload[6] << 8) + payload[7] header["sensorid"] = sensorId # CHECK CRC crc_actual = (payload[-2] << 8) + payload[-1] crc_expected = calcCRC(payload, 5, len(payload) - (5 + 2)) ##trace("crc actual:%s, expected:%s" %(hex(crc_actual), hex(crc_expected))) if crc_actual != crc_expected: raise OpenThingsException("bad CRC") ##return { ## "type": "BADCRC", ## "crc_actual": crc_actual, ## "crc_expected": crc_expected, ## "payload": payload[1:], ##} # DECODE RECORDS i = 8 recs = [] while i < length and payload[i] != 0: # PARAM param = payload[i] wr = ((param & 0x80) == 0x80) paramid = param & 0x7F if paramid in param_info: paramname = (param_info[paramid])["n"] # name paramunit = (param_info[paramid])["u"] # unit else: paramname = "UNKNOWN_" + hex(paramid) paramunit = "UNKNOWN_UNIT" i += 1 # TYPE/LEN typeid = payload[i] & 0xF0 plen = payload[i] & 0x0F i += 1 rec = { "wr": wr, "paramid": paramid, "paramname": paramname, "paramunit": paramunit, "typeid": typeid, "length": plen } if plen != 0: # VALUE valuebytes = [] for x in range(plen): valuebytes.append(payload[i]) i += 1 value = Value.decode(valuebytes, typeid, plen) rec["valuebytes"] = valuebytes rec["value"] = value # store rec recs.append(rec) m = {"type": "OK", "header": header, "recs": recs} if receive_timestamp != None: m["rxtimestamp"] = receive_timestamp return Message(m)
def game_thread(): global deck, deck_ix, initialHands, pl_list, cheated, over, started try: while True: started = False if over: print("\nWaiting for game completion confirmation...") while len(ok) != len(players): sleep(0.5) over = False while len(players) < players_num: sleep(0.5) print("[socket] All", players_num, "players have connected") reset_global_vars() pl_list = list(players.values()) establish_client_sessions(pl_list) wait_for_ok(players_num) deck = shuffle.randomization(pl_list, tiles_per_pl) deck_ix = list(range(len(deck))) shuffle.selection(pl_list, deck_ix) wait_for_ok(players_num - 1) bitcommits = {} # Bit Commitment for ix, pl in enumerate(pl_list): pl.send({ 'type': messages.COMMIT_REQUEST, 'stock': stock, 'deck': deck }) while True: data = pl.recv() if data['type'] != messages.COMMIT_REPLY: raise UnexpectedMessageException() try: pl.rsa_pub_key.verify( bytes.fromhex(data['sig']), hashlib.sha256(bytes.fromhex( data['commit'])).digest(), padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256()) except: print("Ignored COMMIT_FORWARD with invalid signature") continue break bitcommits[pl.name] = data['commit'] for po in pl_list: if po != pl: po.send({ 'type': messages.COMMIT_FORWARD, 'player': ix, 'commit': data['commit'], 'sig': data['sig'] }) print('Waiting') wait_for_ok(players_num - 1) print("Waited") crypto.init(shuffle.iv) deck = [crypto.get_bytes(t) for t in deck] # Decryption for pl in reversed(pl_list): end = (pl == pl_list[0]) pl.send({ 'type': messages.REVELATION_REQUEST, 'end': end, 'reveal': True, 'keys': {} }) data = pl.recv() if data['type'] != messages.REVELATION_REPLY: raise UnexpectedMessageException() for crypt, key in data['keys'].items(): crypt = crypto.get_bytes(crypt) key = crypto.get_bytes(key) dec = crypto.decrypt(crypt, key) try: ix = deck.index(crypt) if end: dec = ast.literal_eval(dec.decode('utf-8')) not_in_stock.append(dec) deck[ ix] = dec # client uses received keys to "decrypt" hand except ValueError as e: pass for po in pl_list: if po != pl: po.send({ 'type': messages.REVELATION_REQUEST, 'end': end, 'reveal': False, 'keys': data['keys'] }) wait_for_ok(players_num - 1) print(" --- Encryption layer from {} successfully removed\n". format(pl.name)) shuffle.sendDeAnon(pl_list) wait_for_ok(players_num) print('[server] Deck distribution is finished') for i, t in enumerate(stock): stock[i] = crypto.get_bytes(t) game = GameFlow(pl_list, tiles_per_pl) started = True numPass = 0 while True: p, action, sig = game.next_play() hand_inc = -1 rec_tile = None if action['ac'] == 'pass': hand_inc = 0 numPass += 1 if len(stock) > 0: cheated = True, 'Server', pl_list[ p].name, 'passing instead of drawing' print( "\n{} just tried to cheat by passing instead of performing a draw!" .format(cheated[2])) finalHands, points, cheaters = validateGame( pl_list, bitcommits, initialHands, game) break elif action['ac'] == 'play': numPass = 0 tileInStock = True for deckElement in deck: if len(deckElement) == 2: if str(shuffle.tiles[deckElement[1]][1]) == str( action['tile']): tileInStock = False break if tileInStock: i = 0 for pl in pl_list: if i == p: cheated = True, 'Server', pl.name, "playing a tile that didn't have" break i += 1 print( "\n{} just tried to cheat by playing a tile that is still on the stock... Aborting game!" .format(cheated[2])) finalHands, points, cheaters = validateGame( pl_list, bitcommits, initialHands, game) break elif action['ac'] == 'draw': rec_tile = crypto.get_bytes(action['tile']) if rec_tile not in stock: # includes empty stock detection print( "\n{} just tried to cheat by drawing a tile that is NOT on the stock... Aborting game!" .format(cheated[2])) finalHands, points, cheaters = validateGame( pl_list, bitcommits, initialHands, game) break i = 0 for pl in pl_list: if i != p: pl.send({ 'type': messages.CONFIRM_ACTION, 'player': p, 'action': action, 'sig': sig }) i += 1 print("\nWaiting for confirmation by all players") wait_for_ok(players_num - 1) if cheated[0]: print("\nCheating complain!") finalHands, points, cheaters = validateGame( pl_list, bitcommits, initialHands, game) break if rec_tile is not None: pl_list[p].send({'type': messages.OK}) stock.remove(rec_tile) new_tile = drawProcess(pl_list[p], rec_tile) hand_inc = 1 game.state.play(Action({ 'ac': 'draw', 'tile': new_tile }, p)) if game.execute_play(hand_inc): break if numPass == players_num: print("\nEveryone passed!") break if not cheated[0]: print("\nGame finished successfully!") finalHands, points, cheaters = validateGame( pl_list, bitcommits, initialHands, game) points = countPoints(finalHands, points) print() for pl in pl_list: print("Player {} got {} points!".format( pl.name, points[pl.name])) pl.send({ 'type': messages.POINTS_VALIDATION_REQUEST, 'final_hands': finalHands, 'points': points, 'cheaters': cheaters, 'penalty': cheating_penalty }) wait_for_ok(players_num, False) for pl in pl_list: pl.send({ 'type': messages.GAME_END, 'reason': 0, 'points': points[pl.name] }) pl.points += points[pl.name] over = True except UnexpectedMessageException: print( "\nUnexpected message received!\n-Game integrity is compromised.\n-Aborting" )
def recv_thread(): global stock, stock_ix, ok, cheated, initialHands, rsa_priv_key, over, game_thr host = "0.0.0.0" crypto.init(shuffle.iv) with open('rsa_private_key', 'rb') as kf: rsa_priv_key = serialization.load_pem_private_key( kf.read(), None, default_backend()) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((host, port)) print("[socket] bound to port", port) s.listen(5) print("[socket] began listening") game_thr = Killable_Thread(target=game_thread) game_thr.start() while True: if len(players) < players_num: read_ready, _, _ = select.select( [p.socket for p in players.values()] + [s], [], [], 1) else: read_ready, _, _ = select.select( [p.socket for p in players.values()], [], []) for sock in read_ready: if sock is s and len(players) < players_num: c, addr = s.accept() print('[socket]', addr[0], ':', addr[1], '-', 'Connected') start_new_thread(register_thread, ( c, addr, )) else: try: rec = b'' while True: part = sock.recv(1024) rec += part if len(part) < 1024: break data = json.loads(rec.decode('ascii')) mac = crypto.encrypt( hashlib.sha256( data['message'].encode('ascii')).digest(), players[sock].session_key) if (mac.hex() if mac is not None else mac) != data['mac']: print("Ignored message with erroneous MAC") continue message = json.loads(data['message']) if message['type'] == messages.ACTION_REPLY: try: players[sock].rsa_pub_key.verify( bytes.fromhex(message['sig']), hashlib.sha256( str(message['action']).encode( 'ascii')).digest(), padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256()) except: print( "Ignored ACTION_REPLY with invalid signature") continue print("[socket]", players[sock].name, "sent message:", data) if message['type'] == messages.ROUTING: if message['destination'] in pseudo_players: dst = message['destination'] del message['destination'] message['source'] = players[sock].name pseudo_players[dst].send(message) elif message['type'] == messages.SELECTION_REPLY: stock_ix = message['tiles'] stock = [deck[ix] for ix in stock_ix] for player in players.values(): if players[sock] is not player: player.send({'type': messages.SELECTION_END}) elif message['type'] == messages.OK: ok.add(players[sock].name) elif message['type'] == messages.DE_ANON_PREP_REPLY: shuffle.de_pseudonymize(list(players.values()), message['array'], deck_ix, deck, stock_ix) elif message['type'] == messages.COMPLAIN: if message['cheater'] is not None: cheated = True, players[sock].name, message[ 'cheater'], 'playing his own tile' else: raise GameEndedException() elif message['type'] == messages.INITIAL_HAND_REPLY: initialHands[ players[sock].name] = message['initial_hand'] elif message['type'] == messages.CLAIM_POINTS_REQUEST: try: cert_pem = bytes.fromhex(message['certificate']) certificate = ssl_crypto.load_certificate( ssl_crypto.FILETYPE_PEM, cert_pem) x509_name = certificate.get_subject( ).get_components() name = x509_name[len(x509_name) - 1][1].decode('ascii') print("\nPlayer", players[sock].name, "is:", name) pubKey_object = certificate.get_pubkey() pubKeyString = ssl_crypto.dump_publickey( ssl_crypto.FILETYPE_PEM, pubKey_object) pubKey = serialization.load_pem_public_key( pubKeyString, None) pubKey.verify(players[sock].signature, bytes(players[sock].name, 'utf-8'), padding.PKCS1v15(), hashes.SHA1()) cert_date = date.today() validate_chain(cert_pem, cert_date) print('\nVerification succeeded - Player', players[sock].name) points_db[name] = points_db[name] + players[ sock].points if name in points_db else players[ sock].points players[sock].send({ 'type': messages.CLAIM_POINTS_REPLY, 'name': name, 'points': points_db[name] }) except: print('\nVerification failed - Player', players[sock].name) players[sock].send({ 'type': messages.CLAIM_POINTS_REPLY, 'name': None, 'points': None }) del players[sock] else: players[sock].message = message players[sock].lock.release() except json.decoder.JSONDecodeError: print( "\nClient unexpectedly disconnected. Restarting game") kill_game(1, sock) except GameEndedException: print( "\nClient reported deck distribution / draw cheating. Restarting game" ) kill_game(2, sock) except: print('comms exception') return
def Main(): global cryptokeys, bitcommits, selfcommit, pseudonym, server_session_key, client_keys, server_pub_key, play_order with open('server_public_key', 'rb') as kf: server_pub_key = serialization.load_pem_public_key( kf.read(), default_backend()) global s s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.connect((addr, port)) except ConnectionRefusedError: print("Error! The server is not online!") return server_session_key, iv, dh_a = server_handshake() crypto.init(crypto.get_bytes(iv)) signature = b'' cert_pem = b'' if not test: pkcs11 = PyKCS11.PyKCS11Lib() if path.exists("/usr/local/lib/libpteidpkcs11.so"): pkcs11.load('/usr/local/lib/libpteidpkcs11.so') else: pkcs11.load('/usr/lib/libpteidpkcs11.so') slots = pkcs11.getSlotList() found_cc = False for slot in slots: if 'CARTAO DE CIDADAO' in pkcs11.getTokenInfo(slot).label: found_cc = True session = pkcs11.openSession(slot) privKey = session.findObjects([ (CKA_CLASS, CKO_PRIVATE_KEY), (CKA_LABEL, 'CITIZEN AUTHENTICATION KEY') ])[0] certificate = session.findObjects( template=[(PyKCS11.CKA_LABEL, "CITIZEN AUTHENTICATION CERTIFICATE" ), (PyKCS11.CKA_CLASS, PyKCS11.CKO_CERTIFICATE)])[0] all_attr = [ e for e in list(PyKCS11.CKA.keys()) if isinstance(e, int) ] attr = session.getAttributeValue(certificate, all_attr) attr = dict(zip(map(PyKCS11.CKA.get, all_attr), attr)) cert_der = x509.load_der_x509_certificate( bytes(attr['CKA_VALUE']), default_backend()) cert_pem = cert_der.public_bytes(Encoding.PEM) while True: pseudonym = 'steve' + str(randint(00000000, 99999999)) send({ 'type': messages.REGISTER_REQUEST, 'sig': bytes( session.sign(privKey, bytes(pseudonym, 'utf-8'), Mechanism(CKM_SHA1_RSA_PKCS))).hex(), 'name': pseudonym }) data = receive() if data['type'] != messages.REGISTER_REPLY: raise UnexpectedMessageException() if data['accepted']: break session.closeSession() if not found_cc: print("\nNo citizenship card connected") return # If Test mode is enabled else: while True: pseudonym = 'steve' + str(randint(00000000, 99999999)) send({ 'type': messages.REGISTER_REQUEST, 'sig': signature.hex(), 'name': pseudonym }) data = receive() if data['type'] != messages.REGISTER_REPLY: raise UnexpectedMessageException() if data['accepted']: break print("Hi I am {}".format(pseudonym)) while True: try: print("Starting new game...") cryptokeys = {} # Ci: Ki bitcommits = {} selfcommit = None client_keys = None play_order, client_keys = clients_handshake(dh_a) numPlayers = len(play_order) data = receive() if data['type'] != messages.RANDOMIZATION_REQUEST: raise UnexpectedMessageException() # --- ENCRYPTION/RANDOMIZATION new_tiles = [] for i, tile in enumerate(data['tiles']): if not isinstance(tile, str): tile = crypto.get_string(str(tile).encode('utf-8')) btile = crypto.get_bytes(tile) collision = True key = None while collision: key = crypto.get_key() cr = crypto.encrypt(btile, key) collision = cr in cryptokeys cryptokeys[cr] = key data['tiles'][i] = crypto.get_string(cr) secret_s = secrets.SystemRandom() secret_s.shuffle(data['tiles']) hand_size = data['tile_num'] final_stocksize = len(data['tiles']) - numPlayers * hand_size next_player = play_order[(play_order.index(pseudonym) + 1) % numPlayers] players_without_self = play_order.copy() players_without_self.pop(players_without_self.index(pseudonym)) randomization_reply = { 'type': messages.RANDOMIZATION_REPLY, 'tiles': data['tiles'] } send(randomization_reply) pseudo_hand = [] data = receive() setup = True # --- SELECTION while setup: if data['type'] != messages.ROUTING and data[ 'type'] != messages.SELECTION_REQUEST and data[ 'type'] != messages.SELECTION_END: raise UnexpectedMessageException() else: if data['type'] == messages.SELECTION_END: if len(pseudo_hand) < hand_size: print( "\nSelection ended before hand was complete. Deck distribution cheating occured!" ) send({'type': messages.COMPLAIN, 'cheater': None}) data = receive() else: setup = False send({'type': messages.OK}) break else: tiles = [] picked = [] if data['type'] == messages.ROUTING: if data['data']['type'] != messages.SELECTION_REPLY: raise UnexpectedMessageException() tiles = data['data']['tiles'] picked = data['data']['picked'] else: tiles = data['tiles'] picked = data['picked'] if len(tiles) == final_stocksize or ( randint(0, 99) < prob_deck_cheating and randint(0, 99) < 50 ): # second randint servers to have even smaller probability action = randint(0, 99) if (action < prob_select_end): setup = False selection_end = { 'type': messages.SELECTION_REPLY, 'tiles': tiles, 'picked': picked } send(selection_end) break action = randint(0, 99) if action < prob_pick_tile and len( pseudo_hand) < hand_size: tile_choice = randint(0, len(tiles) - 1) pic_tile = tiles.pop(tile_choice) pseudo_hand.append(pic_tile) picked.append(pic_tile) else: action = randint(0, 99) if action < prob_swap_tiles and len( pseudo_hand) > 0: number_of_swaps = randint(1, len(pseudo_hand)) for i in range(number_of_swaps): tile_choice = randint(0, len(tiles) - 1) temp_tile = pseudo_hand.pop(i) picked.remove(temp_tile) pic_tile = tiles.pop(tile_choice) pseudo_hand.insert(i, pic_tile) picked.append(pic_tile) tiles.insert(tile_choice, temp_tile) player_selection = randint( 0, len(players_without_self) - 1) selection_reply = { 'type': messages.ROUTING, 'destination': players_without_self[player_selection], 'data': { 'type': messages.SELECTION_REPLY, 'tiles': tiles, 'picked': picked } } send(selection_reply) data = receive() # --- COMMIT index_hand = pseudo_hand.copy() while len(bitcommits) < numPlayers: data = receive() if data['type'] == messages.COMMIT_REQUEST: selfcommit = commit(pseudo_hand) send({ 'type': messages.COMMIT_REPLY, 'commit': selfcommit, 'sig': rsa_priv.sign( hashlib.sha256(bytes.fromhex(selfcommit)).digest(), padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256()).hex() }) stock = data['stock'] pseudo_hand = [ crypto.get_bytes(data['deck'][t_ix]) for t_ix in pseudo_hand ] bitcommits[str(pseudonym)] = None #self commit print("Sent my bit commitment {}".format(selfcommit)) elif data['type'] == messages.COMMIT_FORWARD: bitcommits[play_order[data['player']]] = data['commit'] print("Received bit commitment {} from player {}".format( data['commit'], play_order[data['player']])) send({'type': messages.OK}) else: raise UnexpectedMessageException() for i, t in enumerate(stock): stock[i] = crypto.get_bytes(t) # --- DECRYPTION new_crypts = {} while True: data = receive() if data['type'] != messages.REVELATION_REQUEST: raise UnexpectedMessageException() if data['reveal']: to_send = {} for tile, key in cryptokeys.items(): if (new_crypts != {} and tile in new_crypts.values( )) or ( new_crypts == {} and tile not in stock ): # if first to decrypt, tile not in stock, else new_crypts has stuff in it t_str = crypto.get_string(tile) k_str = crypto.get_string(key) to_send[t_str] = k_str for i, tile in enumerate(pseudo_hand): dec = crypto.decrypt(tile, cryptokeys[tile]) pseudo_hand[i] = dec send({'type': messages.REVELATION_REPLY, 'keys': to_send}) else: new_crypts = {} for crypt, key in data['keys'].items(): crypt = crypto.get_bytes(crypt) key = crypto.get_bytes(key) dec = crypto.decrypt(crypt, key) new_crypts[crypt] = dec try: ix = pseudo_hand.index(crypt) pseudo_hand[ ix] = dec # client uses received keys to "decrypt" hand except ValueError as e: pass send({'type': messages.OK}) if data['end']: break pseudo_hand = [ ast.literal_eval(t.decode('utf-8')) for t in pseudo_hand ] # --- DE-ANON de_anon = True my_private_keys = [] #missing_keys = [t[1] for t in pseudo_hand] missing_keys = index_hand.copy() while de_anon: data = receive() if data['type'] == messages.DE_ANON_REQUEST: break elif data['type'] != messages.ROUTING and data[ 'type'] != messages.DE_ANON_PREP_REQUEST: raise UnexpectedMessageException() else: key_array = [] if data['type'] == messages.ROUTING: if data['data']['type'] != messages.DE_ANON_PREP_REPLY: raise UnexpectedMessageException() key_array = data['data']['array'] else: key_array = data['array'] if key_array.count('0' * 852) <= final_stocksize: action = randint(0, 99) if action < prob_end_deanon: selection_reply = { 'type': messages.DE_ANON_PREP_REPLY, 'array': key_array } send(selection_reply) continue action = randint(0, 99) if action < prob_add_keys and len(missing_keys) > 0: key_choice = randint(0, len(missing_keys) - 1) chosen_key = missing_keys.pop(key_choice) key_array.pop(chosen_key) priv_key, pub_key = crypto.generate_RSA_keys() my_private_keys.append([chosen_key, priv_key]) key_array.insert(chosen_key, pub_key.hex()) cheating_key = randint(0, 27) if randint( 0, 99) < prob_deck_cheating and cheating_key not in ( [a[0] for a in my_private_keys] + missing_keys): priv_key, pub_key = crypto.generate_RSA_keys() key_array.pop(cheating_key) key_array.insert(cheating_key, pub_key.hex()) player_selection = randint(0, len(players_without_self) - 1) selection_reply = { 'type': messages.ROUTING, 'destination': players_without_self[player_selection], 'data': { 'type': messages.DE_ANON_PREP_REPLY, 'array': key_array } } send(selection_reply) if data['type'] != messages.DE_ANON_REQUEST: raise UnexpectedMessageException() encrypted_deck = data['array'] current_hand = [] ki_values = [] try: for i in range(len(my_private_keys)): tmp = ast.literal_eval(my_private_keys[i][1].decrypt( bytes.fromhex(encrypted_deck[my_private_keys[i][0]]), padding.OAEP(padding.MGF1(hashes.SHA256()), hashes.SHA256(), None)).decode('utf-8')) ki_values.append(tmp[0]) current_hand.append(tmp[1]) # Verify hand for i in range(len(current_hand)): try: pseudo = h(ki_values[i], current_hand[i]) except: ValueError() if pseudo not in [a[0] for a in pseudo_hand]: raise ValueError() except ValueError: print( "\nCould not de-anonimize deck. Deck distribution cheating occured!" ) send({'type': messages.COMPLAIN, 'cheater': None}) data = receive() send({'type': messages.OK}) game = Logic(numPlayers, hand_size) drawing = False drawn_tile = None stock_empty = len(stock) <= 0 option = 0 while True: data = receive() if data['type'] == messages.CONFIRM_ACTION: #Send OK, opponent played if data['action']['ac'] == 'draw': rec_tile = crypto.get_bytes(data['action']['tile']) send({'type': messages.OK}) stock.remove(rec_tile) drawProcess(False, rec_tile) stock_empty = len(stock) <= 0 else: print( "Opponent", play_order[data['player']], "played", str(data['action']['tile']) + "!") if data['action']['ac'] == 'play' else print( "Opponent", play_order[data['player']], "passed!") game.playTile( data['action']['tile'], data['action']['right'] ) if data['action']['ac'] == 'play' else game.playPass( ) send( protesting.checkForDuplicatedTiles( game, current_hand, play_order)) #Pos Validation elif data[ 'type'] == messages.ACTION_REQUEST: #Send ACTION_REPLY, this client played print("I'm playing with current hand:", current_hand) tileToPlay = game.chooseValidTile(current_hand) if tileToPlay == None: cheating_chance = randint(0, 99) if cheating_chance < prob_cheat: #Cheating chance = 20% send(cheating.cheatingWithRandomTile(game)) elif not stock_empty: if False and cheating_chance < prob_cheat: print("Fake Pass like Ronaldinho Gaúcho") send(game.playPass()) else: print("No valid play found, gib tile") print(len(stock) - 1) tile_choice = randint(0, len(stock) - 1) drawn_tile = stock.pop(tile_choice) send( game.playDraw( crypto.get_string(drawn_tile))) drawing = True else: print("Passing...") send(game.playPass()) else: send({ 'type': messages.ACTION_REPLY, 'action': { 'ac': 'play', 'tile': tileToPlay[0], 'right': tileToPlay[1] } }) elif data['type'] == messages.OK: if drawing: #This client requested a piece from stock, once all players validate his request, they will enter draw process new_tile = drawProcess(True, drawn_tile) current_hand.append(new_tile) drawing = False draw_tile = None stock_empty = len(stock) <= 0 #stock_empty = True #for testing purposes elif tileToPlay != None: #This client's play validated, removing tile game.playTile(tileToPlay[0], tileToPlay[1]) current_hand.remove(tileToPlay[0]) elif data[ 'type'] == messages.INITIAL_HAND_REQUEST: #Someone complained about cheating!! send({ 'type': messages.INITIAL_HAND_REPLY, 'initial_hand': index_hand }) elif data[ 'type'] == messages.COMMIT_VALIDATION_REQUEST: #Comparison between initial and final bit commits print( "\n\nGame {}! Proceeding to validate everyone's bit commitments for the game recap.\n" .format( "aborted because there was a suspicion of cheating" if data['cheating'] else "finished successfully")) #Generate bit commitment from the data that originated the initial one finalBitcommits = {} for player, hand in data['initial_hands'].items(): if str(player) == str(pseudonym): continue dk = hashlib.sha256() for t in hand: dk.update(t.to_bytes(1, 'big')) finalBitcommits[player] = dk.hexdigest() #Compare both bit commitments for initPlayer, initCommit in bitcommits.items(): for finalPlayer, finalCommit in finalBitcommits.items( ): if str(initPlayer) == str(pseudonym): continue if str(initPlayer) == str(finalPlayer): print( "Comparing initial bit commitment with data sent from {}..." .format(initPlayer), end=" ") if initCommit != finalCommit: print( "\nCould NOT validate {}'s bit commitment! Player sent wrong data" .format(initPlayer)) #Punish player else: print("Validated!".format(initPlayer)) send({'type': messages.OK}) elif data['type'] == messages.POINTS_VALIDATION_REQUEST: print() clientPoints = countPoints(data['final_hands'], data['cheaters'], data['penalty']) for player in clientPoints.keys(): print("Comparing server's score for {}...".format( player), end=" ") if data['points'][player] != clientPoints[player]: print("\nCould NOT validate {}'s points!".format( initPlayer)) else: print("Validated!".format(initPlayer)) send({'type': messages.OK}) elif data['type'] == messages.GAME_END: option = get_end_option(0, data['points']) break except GameEndedException as gee: option = get_end_option(gee.reason, gee.points) if option == 2: claim_points = { 'type': messages.CLAIM_POINTS_REQUEST, 'certificate': cert_pem.hex() } send(claim_points) data = receive() if data['type'] != messages.CLAIM_POINTS_REPLY: raise UnexpectedMessageException() print("\nVerification " + (("confirmed.\nYou are " + data['name'] + " and have a total of " + str(data['points']) + " points.") if data['points'] is not None else "failed.")) print("Closing") # close the connection s.close() return send({"type": messages.OK}) print()