Example #1
0
    def __init__(self):
        print("\nInitializing server...")

        # Load parameters
        with open('parameters', 'rb') as f:
            self.parameters = serialization.load_pem_parameters(
                f.read().strip())

        # Load media files
        self.MEDIA = dict()
        print("\nLoading media...")
        for _, c in CATALOG.items():
            self.MEDIA[c['file_name']] = self.getFile(
                os.path.join(CATALOG_BASE, c['file_name'])).encode('latin')

        # Load private key
        fp = open(FILEPRIVATEKEY, 'rb')
        self.private_key = serialization.load_pem_private_key(fp.read(),
                                                              password=None)
        fp.close()

        # Load certificate
        fc = open(FILECERTIFICATE, "rb")
        self.cert = PKI.getCertFromString(fc.read(), pem=True)
        fc.close()

        # Initialize session dictionary
        self.sessions = {}

        # Initialize pki
        self.pki = PKI()

        print("\nServer has been started!")
def DHexchange(connection):

    serial_parameters = connection.recv(
        1024
    )  #primeste obiectul parametrii serializat de unde genereaa cheia publica si privata DH

    parameters = serialization.load_pem_parameters(
        serial_parameters,
        backend=default_backend())  #dezerializez obiectul parametrii
    serv_private_key = parameters.generate_private_key(
    )  #generaz cheia privata DH
    serv_public_key = serv_private_key.public_key()  #generez cheia publica DH
    serial_serv_public_key = serv_public_key.public_bytes(
        Encoding.PEM,
        PublicFormat.SubjectPublicKeyInfo)  #serializez cheia publica

    connection.send(serial_serv_public_key)  #trimit cheia publica

    serial_client_public_key = connection.recv(
        1024)  #primesc cheia publica client serializata

    client_public_key = serialization.load_pem_public_key(
        serial_client_public_key,
        backend=default_backend())  #deserializez cheiaa
    shared_key = serv_private_key.exchange(
        client_public_key)  #compun cheia comuna DH

    key = HKDF(algorithm=hashes.SHA256(),
               length=32,
               salt=None,
               info=b'handshake data',
               backend=default_backend()).derive(
                   shared_key)  #generezo o cheie noua comuna din cheia DH
    return shared_key, key
    def process_diffie_parameters(self, message: str) -> bool:
        logger.debug("Process Diffie-Hellman Parameters: {}".format(message))

        if self.state == STATE_TO_CIPHER:
            data = base64.b64decode(message.get('data', "").encode())
            self.diffie_parameters = load_pem_parameters(
                data, backend=default_backend())
            self.state = STATE_DIFFIE_PARAMETERS

        else:
            logger.warning("Invalid state. Discarding")
            return False

        try:
            data = message.get('data', None)
            if data is None:
                logger.debug("Invalid message. No data found")
                return False

            bdata = base64.b64decode(message['data'])
        except:
            logger.exception(
                "Could not decode base64 content from message.data")
            return False

        self._send({'type': 'OK'})
        return True
Example #4
0
    def __init__(self):

        # This example DH server uses a known safe prime, or can generate its own if the PEM file is not available.
        # The safe prime used here is ffdhe2048, described here:
        # https://tools.ietf.org/html/rfc7919#appendix-A.1
        # There is nothing strictly wrong with generating your own prime, but this one is well tested.

        import os.path
        pem_path = os.path.join(os.path.dirname(__file__), 'dh_params.pem')
        if not os.path.isfile(pem_path):
            # No PEM file available, generate a new prime of 2048 bits.
            parameters = dh.generate_parameters(generator=2,
                                                key_size=2048,
                                                backend=default_backend())
            s = parameters.parameter_bytes(Encoding.PEM, ParameterFormat.PKCS3)
            with open('dh_params.pem', 'wb') as outfile:
                outfile.write(s)
        else:
            # Load PEM file - a standard format for cryptographic keys and numbers.
            from cryptography.hazmat.primitives.serialization import load_pem_parameters
            with open(pem_path, 'r') as f:
                pem_data = f.read().encode("UTF-8")
                parameters = load_pem_parameters(data=pem_data,
                                                 backend=default_backend())
        self.parameters = parameters
        self.shared_key = None
def deserializeParameters(string, bc=backend):
    """
	Takes a string (some parameters), loading those performing a deserialize operation
	The encoding type was PEM
	"""
    if type(string) == str:
        string = string.encode('utf8')
    return serialization.load_pem_parameters(string, backend=bc)
Example #6
0
    def create_session_key_server(self, message_json, address):

        # decode the params
        parameters = pickle.loads(
            codecs.decode(message_json["params"].encode(), "base64"))
        par = load_pem_parameters(parameters, backend=default_backend())

        # Get the server public key
        manager_pub = message_json["public"].encode()
        save_server_key_client(self._server_path, manager_pub,
                               "/manager_server.pem")

        # Load the key
        r = RSAKGen()
        self.auction_repository.manager_public = r.load_public_key(
            self._server_path, "manager_server.pem")

        # Generate our public/private key
        private_key = par.generate_private_key()
        public_key = private_key.public_key()

        # Get the public key from the user
        peer_public_key_bytes = message_json["pk"].encode()
        peer_public_key = serialization.load_pem_public_key(
            peer_public_key_bytes, default_backend())
        shared_key = private_key.exchange(peer_public_key)

        calendar_date = str(datetime.datetime.now())

        derived_key = HKDF(algorithm=hashes.SHA256(),
                           length=DH_HKDF_KEY,
                           salt=None,
                           info=calendar_date.encode(),
                           backend=default_backend()).derive(shared_key)

        # Construct the message with the keys
        message = "{ \"type\" : \"session_server\" ,\n"

        # Now send our DH public key to the client
        pk_dh = public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo).decode(
                'utf-8')

        message += "\"pk\" : \"" + pk_dh + "\" ,\n"
        message += "\"info\" : \"" + calendar_date + "\",\n"
        message += "\"server_key\" : \"" + self.auction_repository.public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo).decode(
                'utf-8') + "\""

        message += "}"

        # Set the sessionKey as the bytes of the derived_key
        self.auction_repository.session_key_server = derived_key
        return base64.b64encode(message.encode('utf-8'))
Example #7
0
 def __generate_parameters(self):
     param_file = 'dh.pem'
     if not os.path.isfile(param_file):
         self.__parameters = dh.generate_parameters(
             generator=2, key_size=2048, backend=default_backend())
         dh_pem = self.__parameters.parameter_bytes(Encoding.PEM,
                                                    ParameterFormat.PKCS3)
         with open(param_file, 'wb') as output:
             output.write(dh_pem)
     else:
         with open(param_file, 'rb') as binary_file:
             pem_data = binary_file.read()
             self.__parameters = load_pem_parameters(
                 pem_data, default_backend())
     self.__private_key = self.__parameters.generate_private_key()
     self.__full_key = None
Example #8
0
def load_params(bmessage):
    return load_pem_parameters(bmessage, backend=default_backend())
def main(uuid_c):
    print("|--------------------------------------|")
    print("|         SECURE MEDIA CLIENT          |")
    print("|--------------------------------------|\n")

    # Get a list of media files
    print("Contacting Server")
    
    CLIENT_CIPHERSUITS = ["AES256_CBC_SHA256", "AES256_CBC_SHA512", "AES256_GCM_SHA256", "AES256_GCM_SHA512", "ChaCha20_None_SHA256", "ChaCha20_None_SHA512"]

    lib ='/usr/local/lib/libpteidpkcs11.so'
    pkcs11 = PyKCS11.PyKCS11Lib()
    pkcs11.load(lib)
    slots = pkcs11.getSlotList()
    slot = slots[0]
    session = pkcs11.openSession(slot)

    obj = session.findObjects([(PyKCS11.CKA_CLASS, PyKCS11.CKO_CERTIFICATE),
                                        (PyKCS11.CKA_LABEL, 'CITIZEN AUTHENTICATION CERTIFICATE')])[0]
    all_atributes = [PyKCS11.CKA_VALUE]
    attributes = session.getAttributeValue(obj, all_atributes)[0]
    cert = x509.load_der_x509_certificate(bytes(attributes))
    cc_cert_pem = cert.public_bytes(encoding=serialization.Encoding.PEM)


    cc_private_key = session.findObjects([(
                PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY),
                (PyKCS11.CKA_LABEL,'CITIZEN AUTHENTICATION KEY')])[0]


    mechanism = PyKCS11.Mechanism(PyKCS11.CKM_SHA1_RSA_PKCS, None)
    
    data = {"uuid": uuid_c, "client_ciphersuits": CLIENT_CIPHERSUITS, "cc_cert": cc_cert_pem.decode('latin')}        
    data = json.dumps(data)
    signature = bytes(session.sign(cc_private_key, data, mechanism))


    payload = {"data": data, "signature": base64.b64encode(signature).decode('latin')}
    req = requests.get(f'{SERVER_URL}/api/protocols', data= json.dumps(payload))
    req = req.json()

    data_signed = json.loads(req["data"])
    algorithms_modes_digests = data_signed["ciphersuit"].split("_")
    
    algorithm = algorithms_modes_digests[0]
    mode = algorithms_modes_digests[1]
    digest_c = algorithms_modes_digests[2]

    signature = base64.b64decode(req["signature"].encode())

    with open("Certification_Authority.crt", "rb") as CA_cert_file:
        CA_cert = x509.load_pem_x509_certificate(CA_cert_file.read())
        CA_public_key = CA_cert.public_key()

    server_cert = x509.load_pem_x509_certificate(data_signed["server_cert"].encode())
    server_public_key_rsa = server_cert.public_key()

    #Verificar o certificado                                                                                                                       
    CA_public_key.verify(
        server_cert.signature,
        server_cert.tbs_certificate_bytes,
        paddingAsymetric.PKCS1v15(),
        server_cert.signature_hash_algorithm,
    )

    #verificar assinatura
    if digest_c == "SHA256":
        server_public_key_rsa.verify(
            signature,
            req["data"].encode(),
            paddingAsymetric.PSS(
                mgf=paddingAsymetric.MGF1(hashes.SHA256()), 
                salt_length=paddingAsymetric.PSS.MAX_LENGTH
            ),
            hashes.SHA256()         
        )
    elif digest_c == "SHA512":
        server_public_key_rsa.verify(
            signature,
            req["data"].encode(),
            paddingAsymetric.PSS(
                mgf=paddingAsymetric.MGF1(hashes.SHA512()),
                salt_length=paddingAsymetric.PSS.MAX_LENGTH
            ),
            hashes.SHA512()
        )
    else:
        print("Erro")
        sys.exit(0)

    #Certificados do client e private key
    with open("Client_Certificate.pem", "rb") as key_file:
        client_cert_private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
        )

    with open("Client_Certificate.crt", "rb") as cert_file:
        client_cert = cert_file.read()

    data = json.dumps({ "uuid_c": uuid_c, "client_cert": client_cert.decode('latin') })
    
    
    signature = client_sign(digest_c, data)
    payload = { "data": data, "signature": base64.b64encode(signature).decode('latin')}
    req = requests.get(f'{SERVER_URL}/api/key', data=json.dumps(payload))
    req = req.json()

    signature = base64.b64decode(req["signature"].encode())
    message = req["message"].encode()

    req = json.loads(message)

    #Verificar o certificado     
    CA_public_key.verify(
        server_cert.signature,
        server_cert.tbs_certificate_bytes,
        paddingAsymetric.PKCS1v15(),
        server_cert.signature_hash_algorithm,
    )

    #Verificar a assinatura
    if digest_c == "SHA256":
        server_public_key_rsa.verify(
            signature,
            message,
            paddingAsymetric.PSS(
                mgf=paddingAsymetric.MGF1(hashes.SHA256()), 
                salt_length=paddingAsymetric.PSS.MAX_LENGTH
            ),
            hashes.SHA256()         
        )
    elif digest_c == "SHA512":
        server_public_key_rsa.verify(
            signature,
            message,
            paddingAsymetric.PSS(
                mgf=paddingAsymetric.MGF1(hashes.SHA512()),
                salt_length=paddingAsymetric.PSS.MAX_LENGTH
            ),
            hashes.SHA512()
        )
    else:
        print("Erro")
        sys.exit(0)

    
    parameters_pem = req["parameters"].encode()
    server_pub_key_pem = req["server_pub_key"].encode()

    server_pub_key = load_pem_public_key(server_pub_key_pem)
    parameters = load_pem_parameters(parameters_pem)

    client_private_key = parameters.generate_private_key()
    client_pub_key_pem = client_private_key.public_key().public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo
            )

    client_shared_key = client_private_key.exchange(server_pub_key)    
    if digest_c == "SHA256":
        shared_key_derived = HKDF(  
            algorithm=hashes.SHA256(), 
            length=32,
            salt=None,
            info=b'handshake data',
        ).derive(client_shared_key)
    elif digest_c == "SHA512":
        shared_key_derived = HKDF(  
            algorithm=hashes.SHA512(), 
            length=32,
            salt=None,
            info=b'handshake data',
        ).derive(client_shared_key)
    else:
        print("Erro ao derivar a shared key")
        sys.exit(0)   

    data = {"uuid": uuid_c, "client_pub_key": client_pub_key_pem.decode('utf-8')}
    data = json.dumps(data)
    signature = client_sign(digest_c, data)

    payload = {"data": data, "signature": base64.b64encode(signature).decode('latin') }
    req = requests.post(url=f'{SERVER_URL}/api/shared_key', data=json.dumps(payload))


    req = requests.get(f'{SERVER_URL}/api/list')
    if req.status_code == 200:
        print("Got Server List")

    media_list = req.json()

    # Present a simple selection menu    
    idx = 0
    print("MEDIA CATALOG\n")
    for item in media_list:
        print(f'{idx} - {media_list[idx]["name"]}')
    print("----")

    while True:
        selection = input("Select a media file number (q to quit): ")
        if selection.strip() == 'q':
            sys.exit(0)

        if not selection.isdigit():
            continue

        selection = int(selection)
        if 0 <= selection < len(media_list):
            break

    # Example: Download first file
    media_item = media_list[selection]          
    print(f"Playing {media_item['name']}")

    # Detect if we are running on Windows or Linux
    # You need to have ffplay or ffplay.exe in the current folder
    # In alternative, provide the full path to the executable
    if os.name == 'nt':
        proc = subprocess.Popen(['ffplay.exe', '-i', '-'], stdin=subprocess.PIPE)
    else:
        proc = subprocess.Popen(['ffplay', '-i', '-'], stdin=subprocess.PIPE)

    
    # Get data from server and send it to the ffplay stdin through a pipe
    for chunk in range(media_item['chunks'] + 1):

        if algorithm == "AES256":
            if mode == "CBC":
                media_id, iv = encrypt_AES(shared_key_derived, media_item["id"].encode(), "CBC")
                iv = base64.b64encode(iv).decode('latin')
                
                chunk, iv2 = encrypt_AES(shared_key_derived, str(chunk).encode(), "CBC")
                iv2 = base64.b64encode(iv2).decode('latin')

                info = json.dumps({"uuid": uuid_c, "iv": iv, "iv2": iv2})

            elif mode == "GCM":
                media_id, iv, tag1 = encrypt_AES(shared_key_derived, media_item["id"].encode(), "GCM")
                iv = base64.b64encode(iv).decode('latin')
                tag1 = base64.b64encode(tag1).decode('latin')
                
                chunk, iv2, tag2 = encrypt_AES(shared_key_derived, str(chunk).encode(), "GCM")
                iv2 = base64.b64encode(iv2).decode('latin')
                tag2 = base64.b64encode(tag2).decode('latin')

                info = json.dumps({"uuid": uuid_c, "iv": iv, "iv2": iv2, "tag1": tag1, "tag2": tag2})

        elif algorithm == "ChaCha20":
            media_id, nonce = encrypt_ChaCha20(shared_key_derived, media_item["id"].encode())
            nonce = base64.b64encode(nonce).decode('latin')

            chunk, nonce2 = encrypt_ChaCha20(shared_key_derived, str(chunk).encode())
            nonce2 = base64.b64encode(nonce2).decode('latin')
            
            info = json.dumps({"uuid": uuid_c, "nonce": nonce, "nonce_chunk": nonce2})
        else:
            print("erro")
            sys.exit(0)

        media_id = base64.urlsafe_b64encode(media_id).decode('latin')
        chunk = base64.urlsafe_b64encode(chunk).decode('latin')

        signature = client_sign(digest_c, info)
        payload = { "data": info, "signature": base64.b64encode(signature).decode('latin') }

        req = requests.get(f'{SERVER_URL}/api/download?id={media_id}&chunk={chunk}', data=json.dumps(payload))
        req = req.json()

        signature = base64.b64decode(req["signature"].encode())
        #verificar assinatura
        if digest_c == "SHA256":
            server_public_key_rsa.verify(
                signature,
                req["data"].encode(),
                paddingAsymetric.PSS(
                    mgf=paddingAsymetric.MGF1(hashes.SHA256()), 
                    salt_length=paddingAsymetric.PSS.MAX_LENGTH
                ),
                hashes.SHA256()         
            )
        elif digest_c == "SHA512":
            server_public_key_rsa.verify(
                signature,
                req["data"].encode(),
                paddingAsymetric.PSS(
                    mgf=paddingAsymetric.MGF1(hashes.SHA512()),
                    salt_length=paddingAsymetric.PSS.MAX_LENGTH
                ),
                hashes.SHA512()
            )
        else:
            print("Erro")
            sys.exit(0)
        
        req = json.loads(req["data"])

        if algorithm == "AES256":               
            try:
                data_encrypted = req["data"].encode()
                data_encrypted = base64.b64decode(data_encrypted)            
                iv = req["iv"].encode()
                iv = base64.b64decode(iv)
                MAC = req["MAC"].encode()
                MAC = base64.b64decode(MAC)       
                salt = req["salt"].encode()
                salt = base64.b64decode(salt)
            except:
                print(req["error"])
                proc.kill()
                break
                return 0
            
            if digest_c == "SHA256":
                kdf = PBKDF2HMAC(                      
                    algorithm=hashes.SHA256(),
                    length=32,
                    salt=salt,
                    iterations=100000,
                )
                key = kdf.derive(shared_key_derived) 
            elif digest_c == "SHA512":
                kdf = PBKDF2HMAC(                      
                    algorithm=hashes.SHA512(),
                    length=32,
                    salt=salt,
                    iterations=100000,
                )
                key = kdf.derive(shared_key_derived) 
            else:
                print("Erro")
                sys.exit(0)

            c = cmac.CMAC(algorithms.AES(key))
            c.update(data_encrypted)
            c.verify(MAC)

            if mode == "CBC":
                data = decrypt_AES(key, iv, data_encrypted, "CBC")
            elif mode == "GCM":
                tag = req["tag"].encode()
                tag = base64.b64decode(tag)
                data = decrypt_AES(key, iv, data_encrypted, "GCM", tag)

        
            info = json.loads(data.decode('latin'))

            data = info["data"]
            data = binascii.a2b_base64(data)
            

        elif algorithm == "ChaCha20":                                                   #CHACHA20 Funciona
            try:
                nonce = req["nonce"].encode()
                nonce = base64.b64decode(nonce)
                data_encrypted = req["data"].encode()
                data_encrypted = base64.b64decode(data_encrypted)
                MAC = req["MAC"].encode()
                MAC = base64.b64decode(MAC)
                salt = req["salt"].encode()
                salt = base64.b64decode(salt)
                
            except:
                print(req["error"])
                proc.kill()
                break
                return 0

            if digest_c == "SHA256":
                kdf = PBKDF2HMAC(                       
                    algorithm=hashes.SHA256(),
                    length=32,
                    salt=salt,
                    iterations=100000,
                )
                key = kdf.derive(shared_key_derived)

                h = hmac.HMAC(key, hashes.SHA256())
                h.update(data_encrypted)
                h.verify(MAC)
            elif digest_c == "SHA512":
                kdf = PBKDF2HMAC(                      
                    algorithm=hashes.SHA512(),
                    length=32,
                    salt=salt,
                    iterations=100000,
                )
                key = kdf.derive(shared_key_derived)

                h = hmac.HMAC(key, hashes.SHA512())
                h.update(data_encrypted)
                h.verify(MAC)

            else:
                print("ERRO")
                sys.exit(0)

            data = decrypt_ChaCha20(key, nonce, data_encrypted)
            info = json.loads(data.decode('latin'))

            data = info["data"]
            data = binascii.a2b_base64(data)
        else:
            print("Erro")
            sys.exit(0)


        try:
            proc.stdin.write(data)
        except:
            break
Example #10
0
    def create_session_key_user_server(self, message_json):
        rsa_kg = RSAKGen()
        username = message_json["username"]
        random_key = rsa_kg.decipher_with_private_key(
            self.auction_repository.private_key,
            base64.b64decode(message_json["random_key"]))
        # VERIFIES THE message integrity
        if not HMAC_Conf.verify_function("message", message_json, random_key):
            return base64.b64encode(
                "{ \"type\" : \"Tempered data\"}".encode('utf-8'))

        # Decrypts the message
        data = decrypt_data("", message_json["message"],
                            base64.b64decode(message_json["iv"]),
                            base64.b64decode(message_json["Key"]),
                            self.auction_repository.private_key)

        # Loads the messsage to json
        internal_json = json.loads(data, strict="False")
        rsa_signature = internal_json["rsa_signature"]
        certificate = base64.b64decode(
            internal_json["certificate"]
        )  #PENSO QUE TENS DE FAZER BASE64 DECODE
        digital_signature = base64.b64decode(
            internal_json["digital_signature"])
        citizen = CitizenCard()
        certificate = x509.load_pem_x509_certificate(certificate,
                                                     default_backend())
        if not citizen.check_signature(
                certificate, digital_signature,
                self.auction_repository.clients_challenge[base64.b64decode(
                    message_json["username"])].encode()):
            return base64.b64encode(
                "{ \"type\" : \"No valid signature\"}".encode('utf-8'))

        if not citizen.validate_certificate(certificate):
            return base64.b64encode(
                "{ \"type\" : \"No valid certificate\"}".encode('utf-8'))

        self.auction_repository.clients_challenge.pop(
            base64.b64decode(message_json["username"]))

        # Get the parameters
        parameters = pickle.loads(
            codecs.decode(message_json["params"].encode(), "base64"))
        par = load_pem_parameters(parameters, backend=default_backend())

        # Generate our DH public/private key
        private_key = par.generate_private_key()
        public_key = private_key.public_key()

        # Get the public key bytes from the user
        peer_public_key_bytes = message_json["pk"].encode()
        peer_public_key = serialization.load_pem_public_key(
            peer_public_key_bytes, default_backend())

        calendar_date = str(datetime.datetime.now())

        shared_key = private_key.exchange(peer_public_key)
        derived_key = HKDF(algorithm=hashes.SHA256(),
                           length=DH_HKDF_KEY,
                           salt=None,
                           info=calendar_date.encode("utf-8"),
                           backend=default_backend()).derive(shared_key)

        # Construct the message with the keys
        message = "{ \"type\" : \"session\" ,\n"

        # Now send our DH public key to the client
        pk_dh = public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo).decode(
                'utf-8')

        message += "\"pk\" : \"" + pk_dh + "\" ,\n"
        message += "\"info\" : \"" + calendar_date + "\",\n"
        message += "\"server_key\" : \"" + self.auction_repository.public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo).decode(
                'utf-8') + "\""

        message += "}"
        # Get the username and set the session key
        self.auction_repository.session_key_clients[
            message_json["username"]] = derived_key
        # Get the public key from the user key from the user
        _dir = os.getcwd() + "/Clients/" + message_json["username"]
        if not check_directory(_dir):
            if not check_directory(os.getcwd() + "/Clients"):
                os.mkdir(os.getcwd() + "/Clients")
            os.mkdir(_dir)
            with open(_dir + "/" + PK_NAME, "wb") as file:
                file.write(message_json["public"].encode("utf-8"))

        return base64.b64encode(message.encode('utf-8'))
    def test_wrong_parameters_format(self, backend):
        param_data = b"---- NOT A KEY ----\n"

        with pytest.raises(ValueError):
            load_pem_parameters(param_data, backend)
Example #12
0
def on_message(client, userdata, msg):
    global parameters
    global hmac_key
    global b_public_key_number
    global a_private_key
    global a_public_key
    global a_private_key_ecdh
    global a_public_key_ecdh
    global key_fernet
    global f_key
    global a_key

    if (msg.topic == (name + "/to")):

        # Receive params
        if (str(msg.payload.decode()).split(":")[0] == "param"):
            b_pem = str(msg.payload.decode()).split(":")[1]
            parameters = load_pem_parameters(bytes(b_pem, 'ascii'),
                                             backend=default_backend())

            # Calculate keys from params
            a_private_key = parameters.generate_private_key()
            a_public_key = a_private_key.public_key()
            client.publish(name + "/from",
                           "public:" + str(a_public_key.public_numbers().y))

        # Receive public key
        elif (str(msg.payload.decode()).split(":")[0] == "public"):
            print("Public key received from platform.")

            # DH
            if (asymmetric_mode == 0):
                b_public_key_number = int(
                    str(msg.payload.decode()).split(":")[1])
                peer_public_numbers = dh.DHPublicNumbers(
                    b_public_key_number, parameters.parameter_numbers())
                b_public_key = peer_public_numbers.public_key(
                    default_backend())
                # Calculate shared key
                a_shared_key = a_private_key.exchange(b_public_key)

            # ECDH
            else:
                # Generate private and public key ECDH
                a_private_key_ecdh = ec.generate_private_key(ec.SECP384R1())
                a_public_key_ecdh = a_private_key_ecdh.public_key()

                b_public_key_number = str(msg.payload.decode()).split(":")[1]
                b_public_key = load_pem_public_key(
                    b_public_key_number.encode())
                client.publish(
                    name + "/from", "public:" + a_public_key_ecdh.public_bytes(
                        encoding=Encoding.PEM,
                        format=PublicFormat.SubjectPublicKeyInfo).decode())
                # Calculate shared key
                a_shared_key = a_private_key_ecdh.exchange(
                    ec.ECDH(), b_public_key)

            print("Shared key calculated.")

            # Calculate HMAC
            def hebra():
                global hmac_key
                # If device has just 'input', write HMAC key here
                if (mode == 0):
                    hmac_key = str(
                        input(
                            "Introduce la clave que aparece en la plataforma: "
                        ))
                # If device has 'output' or nothing, write HMAC key on web page
                else:
                    hmac_key = str(os.urandom(2).hex())
                print("HMAC KEY: " + hmac_key)

                # Calcula HMAC (DH or ECDH)
                if (asymmetric_mode == 0):
                    h = hmac.new(
                        bytes(hmac_key, 'ascii'),
                        bytes(str(a_public_key.public_numbers().y), 'ascii'),
                        hashlib.sha256)
                else:
                    h = hmac.new(
                        bytes(hmac_key, 'ascii'),
                        a_public_key_ecdh.public_bytes(
                            encoding=Encoding.PEM,
                            format=PublicFormat.SubjectPublicKeyInfo),
                        hashlib.sha256)

                # Send to platform HMAC
                client.publish(name + "/from", "hmac:" + str(h.hexdigest()))

            if (hmac_key is None):
                # New thread because 'input' is blocking
                threading.Thread(target=hebra).start()

            # Calculate FERNET key using HASH
            derived_key_fernet = HKDF(
                algorithm=hashes.SHA256(),
                length=32,
                salt=None,
                info=b'handshake data').derive(a_shared_key)
            key_fernet = base64.urlsafe_b64encode(derived_key_fernet)
            f_key = Fernet(key_fernet)

            # Calculate AEAD key using HASH
            derived_key_aead = HKDF(
                algorithm=hashes.SHA256(),
                length=24,
                salt=None,
                info=b'handshake data').derive(a_shared_key)
            key_aead = base64.urlsafe_b64encode(derived_key_aead)
            a_key = aead.AESGCM(key_aead)