Пример #1
0
 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
Пример #2
0
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 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
Пример #4
0
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
Пример #5
0
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)
Пример #6
0
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
Пример #7
0
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)
Пример #8
0
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"
        )
Пример #9
0
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
Пример #10
0
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()