def encrypt_with_public_key(self, key, data):
        """
        info: Encrypts data with public key.
        :param key: bytes or ras key object
        :param data: bytes
        :return: bytes: encrypted data
        """

        if not isinstance(key, bytes):
            public_key = key
        else:
            public_key = serialization.load_pem_public_key(key, backend=default_backend())

        # setup aes
        aes_raw_key = aead.AESGCM.generate_key(bit_length=self._aes_key_size)
        aes_key = aead.AESGCM(aes_raw_key)
        nonce = os.urandom(self._nonce_key_size)

        # get random data size
        random_data_size = self._random.randint(0, self._max_random)
        raw_random_data_size = random_data_size.to_bytes(length=self._random_header_size,
                                                         byteorder="big",
                                                         signed=False)

        # AES encrypt data [random header][random data][data]
        data = raw_random_data_size + os.urandom(random_data_size) + data
        encrypted_data = aes_key.encrypt(nonce, data, self._authenticator)

        # RSA encrypt [AES key][nonce][sandy check key]
        rsa_header = aes_raw_key + nonce + Encryption.make_sandy_check_key(data)
        encrypted_rsa_header = public_key.encrypt(rsa_header, padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),
                                                                           algorithm=hashes.SHA256(),
                                                                           label=None))

        return encrypted_rsa_header + encrypted_data
Exemple #2
0
def Encrypt(stream: IO[bytes], key: bytes) -> IO[bytes]:
    """Encrypts given file-like object using AES algorithm with GCM mode.

  The input stream is divided into small chunks of predefined size and then each
  chunk is encrypted using AES GCM procedure. In the encoded stream, before each
  proper chunk there is a nonce binary string prepended. As associated data for
  each encrypted chunk, chunk index and information about whether it is the last
  chunk is used.

  Args:
    stream: A file-like object to encrypt.
    key: A secret key used for encrypting the data.

  Returns:
    A file-like object with encrypted data.
  """
    aesgcm = aead.AESGCM(key)

    def Generate() -> Iterator[bytes]:
        chunks = ioutil.Chunk(stream, size=_AEAD_CHUNK_SIZE)
        chunks = iterator.Lookahead(enumerate(chunks))

        for idx, chunk in chunks:
            nonce = os.urandom(_AEAD_NONCE_SIZE)
            adata = _AEAD_ADATA_FORMAT.pack(idx, chunks.done)

            yield nonce + aesgcm.encrypt(nonce, chunk, adata)

    return ioutil.Unchunk(Generate())
Exemple #3
0
def TestInput(input_bytes):
    if len(input_bytes) < 12:
        return

    fdp = atheris.FuzzedDataProvider(input_bytes)

    choice = fdp.ConsumeIntInRange(1, 4)

    if choice == 1:
        cipher = aead.ChaCha20Poly1305(aead.ChaCha20Poly1305.generate_key())
    if choice == 2:
        cipher = aead.AESGCM(aead.AESGCM.generate_key(bit_length=128))
    if choice == 3:
        cipher = aead.AESOCB3(aead.AESOCB3.generate_key(bit_length=128))
    if choice == 4:
        cipher = aead.AESCCM(aead.AESCCM.generate_key(bit_length=128))

    msg = fdp.ConsumeBytes(32)
    authentext = fdp.ConsumeBytes(32)
    nonce = fdp.ConsumeBytes(12)

    if len(nonce) < 12:
        return

    ciphertext = cipher.encrypt(nonce, msg, authentext)
    plaintext = cipher.decrypt(nonce, ciphertext, authentext)

    assert (plaintext == msg), "Encryption/Decrption error!"
Exemple #4
0
 def encrypt(self, plain_text, aad=None):
     plain_text = ensure_binary(plain_text)
     try:
         iv = get_random_bytes(algorithms.AES.block_size // 8)
         mode = self._mode(iv)
         if mode.name == "GCM":
             cipher = aead.AESGCM(self._key)
             cipher_text_and_tag = cipher.encrypt(iv, plain_text, aad)
             cipher_text = cipher_text_and_tag[:len(cipher_text_and_tag) -
                                               16]
             auth_tag = cipher_text_and_tag[-16:]
         else:
             cipher = Cipher(algorithms.AES(self._key),
                             mode,
                             backend=default_backend())
             encryptor = cipher.encryptor()
             padder = PKCS7(algorithms.AES.block_size).padder()
             padded_data = padder.update(plain_text)
             padded_data += padder.finalize()
             cipher_text = encryptor.update(
                 padded_data) + encryptor.finalize()
             auth_tag = None
         return iv, cipher_text, auth_tag
     except Exception as e:
         raise JWEError(e)
    def decrypt(self, cipher_text, iv=None, aad=None, tag=None):
        cipher_text = six.ensure_binary(cipher_text)
        try:
            iv = six.ensure_binary(iv)
            mode = self._mode(iv)
            if mode.name == "GCM":
                if tag is None:
                    raise ValueError("tag cannot be None")
                cipher = aead.AESGCM(self._key)
                cipher_text_and_tag = cipher_text + tag
                try:
                    plain_text = cipher.decrypt(iv, cipher_text_and_tag, aad)
                except InvalidTag:
                    raise JWEError("Invalid JWE Auth Tag")
            else:
                cipher = Cipher(algorithms.AES(self._key), mode,
                                backend=default_backend())
                decryptor = cipher.decryptor()
                padded_plain_text = decryptor.update(cipher_text)
                padded_plain_text += decryptor.finalize()
                unpadder = PKCS7(algorithms.AES.block_size).unpadder()
                plain_text = unpadder.update(padded_plain_text)
                plain_text += unpadder.finalize()

            return plain_text
        except Exception as e:
            raise JWEError(e)
Exemple #6
0
def get_aead_cipher(key, method):
    '''get_aead_cipher
       method should be AEAD method'''
    if method.startswith('aes'):
        return aead.AESGCM(key)
    try:
        return aead.ChaCha20Poly1305(key)
    except Exception:
        from .ctypes_libsodium import SodiumAeadCrypto
        return SodiumAeadCrypto(method, key)
Exemple #7
0
  def testIncorrectTag(self):
    key = os.urandom(32)
    aesgcm = crypto_aead.AESGCM(key)

    buf = io.BytesIO()

    nonce = os.urandom(aead._AEAD_NONCE_SIZE)
    buf.write(nonce)
    buf.write(aesgcm.encrypt(nonce, b"foo", b"QUUX"))
    buf.seek(0, io.SEEK_SET)

    with self.assertRaises(crypto_exceptions.InvalidTag):
      aead.Decrypt(buf, key).read()
Exemple #8
0
  def testIncorrectData(self):
    key = os.urandom(32)
    aesgcm = crypto_aead.AESGCM(key)

    buf = io.BytesIO()

    nonce = os.urandom(aead._AEAD_NONCE_SIZE)
    adata = aead._AEAD_ADATA_FORMAT.pack(0, True)
    buf.write(nonce)
    buf.write(aesgcm.encrypt(nonce, b"foo", adata))
    buf.getbuffer()[-1] ^= 0b10101010  # Corrupt last byte.
    buf.seek(0, io.SEEK_SET)

    with self.assertRaises(crypto_exceptions.InvalidTag):
      aead.Decrypt(buf, key).read()
Exemple #9
0
    def salvar(self, evt):
        dados_para_salvar = self.__tipoAlteracaoEscolhida
        dados_para_salvar += ":" + self.__formulaHashOuCriptoEscolhida
        if self.__tipoAlteracaoEscolhida in self.__tiposAlteracoes:
            if self.__tipoAlteracaoEscolhida == 'Criptografia':
                dados_para_salvar += ":" + self._txtChave.GetValue()
                dados_para_salvar += ":" + self._txtIV.GetValue()
                dados_para_salvar += ":" + self._txtEntradaDados.GetValue()
            elif self.__tipoAlteracaoEscolhida == 'Hmac':
                dados_para_salvar += ":" + self._txtChave.GetValue()
                dados_para_salvar += ":" + self._txtEntradaDados.GetValue()

            dialogNome = wx.TextEntryDialog(
                self,
                _('Escolha um nome para o conjunto de dados de criptografia:'),
                caption=_('Salvar Configurações de Criptografia'))
            dialogNome.ShowModal()
            dialogSenha = wx.TextEntryDialog(
                self,
                _('Entre uma senha para criptografar essas configurações, ela será necessária '
                  'para carregar essas informações mais tarde:'),
                caption=_('Salvar configurações de Criptografia'))
            dialogSenha.ShowModal()
            nome = dialogNome.GetValue()
            senha = dialogSenha.GetValue()
            iteracoes = 1000000
            if senha is not '' or None:
                salt = os.urandom(32)
                senha = senha.encode()
                dados_para_salvar = dados_para_salvar.encode()
                chave = PBKDF2HMAC(algorithm=hashes.SHA3_256,
                                   length=32,
                                   salt=salt,
                                   iterations=1000000,
                                   backend=default_backend()).derive(senha)
                nounce = os.urandom(16)
                aesgcm = aead.AESGCM(chave)
                dados_criptografados = aesgcm.encrypt(nounce,
                                                      dados_para_salvar, None)
                dados_para_salvar = salt + dados_criptografados + nounce
                ts = datetime.now()
                arquivo = shelve.open('cyfer')
                arquivo[nome] = {
                    "data": ts,
                    'iterações': iteracoes,
                    'dados': dados_para_salvar
                }
                arquivo.close()
Exemple #10
0
def Decrypt(stream: IO[bytes], key: bytes) -> IO[bytes]:
    """Decrypts given file-like object using AES algorithm in GCM mode.

  Refer to the encryption documentation to learn about the details of the format
  that this function allows to decode.

  Args:
    stream: A file-like object to decrypt.
    key: A secret key used for decrypting the data.

  Returns:
    A file-like object with decrypted data.
  """
    aesgcm = aead.AESGCM(key)

    def Generate() -> Iterator[bytes]:
        # Buffered reader should accept `IO[bytes]` but for now it accepts only
        # `RawIOBase` (which is a concrete base class for all I/O implementations).
        reader = io.BufferedReader(stream)  # pytype: disable=wrong-arg-types

        # We abort early if there is no data in the stream. Otherwise we would try
        # to read nonce and fail.
        if not reader.peek():
            return

        for idx in itertools.count():
            nonce = reader.read(_AEAD_NONCE_SIZE)

            # As long there is some data in the buffer (and there should be because of
            # the initial check) there should be a fixed-size nonce prepended to each
            # chunk.
            if len(nonce) != _AEAD_NONCE_SIZE:
                raise EOFError(f"Incorrect nonce length: {len(nonce)}")

            chunk = reader.read(_AEAD_CHUNK_SIZE + 16)

            # `BufferedReader#peek` will return non-empty byte string if there is more
            # data available in the stream.
            is_last = reader.peek() == b""  # pylint: disable=g-explicit-bool-comparison

            adata = _AEAD_ADATA_FORMAT.pack(idx, is_last)

            yield aesgcm.decrypt(nonce, chunk, adata)

            if is_last:
                break

    return ioutil.Unchunk(Generate())
Exemple #11
0
 def tentar_abrir_arq_config(self, nome):
     try:
         arq = shelve.open('cyfer')
         dados = arq[nome]['dados']
         arq.close()
         salt = dados[:32]
         nounce = dados[-16:]
         dados = dados[32:len(dados) - 16]
         dialogSenha = wx.TextEntryDialog(
             self,
             _('Entre a senha para decriptografar essas configurações:'),
             caption=_('Carregar configurações de Criptografia'))
         dialogSenha.ShowModal()
         senha = dialogSenha.GetValue().encode()
         chave = PBKDF2HMAC(algorithm=hashes.SHA3_256,
                            length=32,
                            salt=salt,
                            iterations=1000000,
                            backend=default_backend()).derive(senha)
         dados_decriptografados = aead.AESGCM(chave).decrypt(
             nounce, dados, None)
         dados_separados = dados_decriptografados.decode().split(':')
         if dados_separados[0] == 'Criptografia':
             self.comboBoxTipoAlteracao.SetSelection(0)
             self.eventoComboBoxTipoAlteracao(None)
             self.comboBoxFormulaHashOuCripto.SetSelection(
                 self._Criptografia_para_escolher.index(dados_separados[1]))
             self.eventoComboBoxFormulaHashOuCripto(None)
             self._txtChave.Clear()
             self._txtChave.WriteText(dados_separados[2])
             self._txtIV.Clear()
             self._txtIV.WriteText(dados_separados[3])
             self._txtEntradaDados.Clear()
             self._txtEntradaDados.WriteText(dados_separados[4])
         elif dados_separados[0] == 'Hmac':
             self.comboBoxTipoAlteracao.SetSelection(2)
             self.eventoComboBoxTipoAlteracao(None)
             self.comboBoxFormulaHashOuCripto.SetSelection(
                 self._Hmac_para_escolher.index(dados_separados[1]))
             self.eventoComboBoxFormulaHashOuCripto(None)
             self._txtChave.Clear()
             self._txtChave.WriteText(dados_separados[2])
             self._txtEntradaDados.Clear()
             self._txtEntradaDados.WriteText(dados_separados[3])
     except KeyError:
         self.error_dialog(
             _('Não há nenhum arquivo com esse nome que possua configurações para esse programa.'
               ))
    def decrypt(self, data):
        """
        info: Decrypt message. The data will also be checked for corruption.
        :param data: bytes
        :return: bytes: Unencrypted data.
        """

        try:
            # decrypt rsa block
            rsa_block = self._private_key.decrypt(data[:self._rsa_block_size],
                                                  padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),
                                                               algorithm=hashes.SHA256(),
                                                               label=None))

            # get aes key
            aes_raw_key = rsa_block[:-(self._nonce_key_size + self._sha_block_size)]
            # get nonce
            nonce = rsa_block[-(self._nonce_key_size + self._sha_block_size):-self._sha_block_size]
            # get sandy check key
            sandy_check_key = rsa_block[-self._sha_block_size:]
            # make aes decrypter
            aes_key = aead.AESGCM(aes_raw_key)

            # get aes encrypted data
            data = data[self._rsa_block_size:]

            # get unencrypted ase data
            data = aes_key.decrypt(nonce, data, self._authenticator)

            # sandy check data
            if not Encryption.check_sandy_check_key(sandy_check_key, data):
                raise EncryptionError("Sandy Check Failed")

            # remove random bytes
            random_data_size = int.from_bytes(data[:self._random_header_size], byteorder="big", signed=False)

            return data[random_data_size + self._random_header_size:]

        except Exception as e:
            raise EncryptionError("Cant decrypt: {0}".format(e))
def _decrypt_report_id(guid):
    """Decrypt and verify the report id.

    Returns
    -------
    str or None
        Decrypted id if successfully decrypted, None otherwise.

    """
    config = get_config()
    key = base64.b64decode(config.get(CONF_SECTION, CONF_KEY_KEY))

    # https://cryptography.io/en/latest/hazmat/primitives/aead/
    aes_gcm = aead.AESGCM(key)

    try:
        encoded_nonce, encoded_data = guid.split(',', 1)
        nonce = base64.b64decode(encoded_nonce)
        encypted_data = base64.b64decode(encoded_data)
        return aes_gcm.decrypt(nonce, encypted_data, None)
    except (ValueError, TypeError, InvalidTag):
        print('Invalid guid given to resolveReport:', guid, file=sys.stderr)
        return None
def main():
    data = sys.stdin.read().encode()

    key = aead.AESGCM.generate_key(bit_length=256)
    aesgcm = aead.AESGCM(key)
    nonce = os.urandom(12)
    ct = aesgcm.encrypt(nonce, data, b'')

    data = {
        "nonce": base64.b64encode(nonce).decode(),
        "ct": base64.b64encode(ct).decode(),
        "key": "",
    }

    for key_id, credential_id, public_key in iter_keys():
        print("Encrypt for %s" % key_id)
        kct = public_key.encrypt(
            key,
            padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),
                         algorithm=hashes.SHA256(),
                         label=None))
        data["key"] = base64.b64encode(kct).decode()
        with open("database/%s" % credential_id.replace("/", "_"), "w") as out:
            out.write(json.dumps(data))
Exemple #15
0
def on_message(client, userdata, msg):
    global name
    global mode
    global asymmetric_mode
    global symmetric_mode
    global b_public_key
    global b_public_key_ecdh
    global a_shared_key
    global f_key
    global a_key
    global h
    global hmac_key
    print(msg.topic + " -> " + str(msg.payload.decode()))

    # Connection message
    if (msg.topic == "connection"):
        print("Connection message.")

        # Initialize some variables
        name = str(msg.payload.decode()).split(":")[0]
        mode = str(msg.payload.decode()).split(":")[1]
        asymmetric_mode = int(str(msg.payload.decode()).split(":")[2])
        symmetric_mode = int(str(msg.payload.decode()).split(":")[3])

        # Save the variables for each device
        names.append(name)
        modes.append(mode)
        asymmetric_modes.append(asymmetric_mode)
        symmetric_modes.append(symmetric_mode)

        # Key exchange
        if (asymmetric_mode == 0):
            client.publish(name + "/to", "param:" + str(params_pem, 'ascii'))
            client.publish(name + "/to",
                           "public:" + str(a_public_key.public_numbers().y))
        else:
            client.publish(
                name + "/to", "public:" + a_public_key_ecdh.public_bytes(
                    encoding=Encoding.PEM,
                    format=PublicFormat.SubjectPublicKeyInfo).decode())
        client.subscribe(name + "/from")
        print("Public key sent to device.")

        # Show the information on web page
        data = {
            "type": "conexion_dispositivo",
            "payload": msg.payload.decode()
        }
        server.send_message_to_all(json.dumps(data))

    # Message from a device that has already connected
    else:

        # Topic = <Device name>/from
        name = str(msg.topic).split("/")[0]
        if (msg.topic == (name + "/from")):

            # Receive device public key
            if (str(msg.payload.decode()).split(":")[0] == "public"):

                # If we know its old public key, we upload it
                try:
                    public_keys.pop(names.index(name))
                    shared_keys.pop(names.index(name))
                    fernet_keys.pop(names.index(name))
                    aead_keys.pop(names.index(name))
                    print("Public key uploaded from device.")

                # If we don't know its public key, we save it
                except IndexError:
                    # If the device has 'output' or nothing
                    if (int(mode) > 0):
                        # HMAC key will be introduced on web page
                        data = {"type": "hmac", "name": name, "mode": mode}

                    # If the device has just 'input'
                    else:
                        # HMAC key will be introduced on device
                        hmac_key = str(os.urandom(2).hex())
                        data = {
                            "type": "hmac",
                            "name": name,
                            "mode": mode,
                            "hmac_key": hmac_key
                        }

                    # Show the correspondent information on web page
                    server.send_message_to_all(json.dumps(data))

                    print("New public key from device.")

                # DH key exchange
                if (asymmetric_modes[names.index(name)] == 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())
                    public_keys.insert(names.index(name), b_public_key)
                    a_shared_key = a_private_key.exchange(b_public_key)

                # ECDH key exchange
                else:
                    b_public_key_number = str(
                        msg.payload.decode()).split(":")[1]
                    b_public_key_ecdh = load_pem_public_key(
                        b_public_key_number.encode())
                    a_shared_key = a_private_key_ecdh.exchange(
                        ec.ECDH(), b_public_key_ecdh)

                print("Shared key calculated.")
                # Save the shared key
                shared_keys.insert(names.index(name), a_shared_key)

                # We fix the shared key for Fernet using HASH and save it
                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)
                fernet_keys.insert(names.index(name), f_key)

                # We fix the shared key for AEAD using HASH and save it
                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)
                aead_keys.insert(names.index(name), a_key)

            # Receive HMAC from device
            elif (str(msg.payload.decode()).split(":")[0] == "hmac"):

                # Save received HMAC
                print("HMAC recibida del dispositivo.")
                h = str(msg.payload.decode()).split(":")[1]

                # If the device has just 'input'
                if (int(mode) == 0):
                    # DH or ECDH
                    if (asymmetric_mode == 0):
                        h2 = hmac.new(
                            bytes(hmac_key, 'ascii'),
                            bytes(str(b_public_key.public_numbers().y),
                                  'ascii'), hashlib.sha256)
                    else:
                        h2 = hmac.new(
                            bytes(hmac_key, 'ascii'),
                            b_public_key_ecdh.public_bytes(
                                encoding=Encoding.PEM,
                                format=PublicFormat.SubjectPublicKeyInfo),
                            hashlib.sha256)

                    # Compare HMAC
                    if (hmac.compare_digest(h, h2.hexdigest())):
                        hmacs.append(True)
                        data = {
                            "type": "datos_dispositivos",
                            "name": name,
                            "mode": mode
                        }
                        # Device will be added on web page if HMAC is correct
                        server.send_message_to_all(json.dumps(data))
                        print("HMAC de " + name +
                              " coincide. Añadiendo dispositivo...")
                    else:
                        hmacs.append(False)
                        # Unsubscribe
                        client.unsubscribe(name + "/from")
                        print("HMAC de " + name +
                              " no coincide. Dispositivo expulsado.")

            # Receive message from device
            elif (str(msg.payload.decode()).split(": ")[0] == "message"):
                message = str(msg.payload.decode()).split(": ")[1]

                # Fernet or AEAD
                if (symmetric_modes[names.index(name)] == 0):
                    message = fernet_keys[names.index(name)].decrypt(
                        message.encode())
                else:
                    message = aead_keys[names.index(name)].decrypt(
                        b"12345678", message.encode('latin-1'), None)
                print("Message from " + name + ": " + message.decode())

                # Show the correspondent information on web page
                data = {
                    "type": "message",
                    "name": name,
                    "payload": message.decode()
                }  # model data
                server.send_message_to_all(json.dumps(data))
Exemple #16
0
# prompt for input on stderr
def einput(text):
    sys.stderr.write(text)
    return input()


# getpass and encode
passwd = lambda prompt='Enter Password: '******'utf-8')

# split string at index
split = lambda string, index: (string[:index], string[index:])

# get aead cipher depending on arguments
aead = lambda key, cipher: AEAD.AESGCM(key) if cipher == Aenker.cipher_t.Value(
    'AESGCM') else AEAD.ChaCha20Poly1305(key)


# key derivation wrappers
class KDF:

    argon2 = lambda password, blob :\
      split(argon2_raw(password, blob.nonce, \
      blob.kdf_opts.time_cost, 2**blob.kdf_opts.memory_cost, blob.kdf_opts.parallelism, \
      hash_len=44, type=argon2_type.I), 12)


# parse commandline arguments
argparser = argparse.ArgumentParser()
Exemple #17
0
 def __init__(self, key):
     assert len(key) * 8 == self.KEY_BITS
     self._cipher = aead.AESGCM(key)
     self.key = key
Exemple #18
0
 def decrypt(cls, ciphertext_and_tag, aad, key, iv):
     try:
         return aead.AESGCM(key).decrypt(iv, ciphertext_and_tag, aad)
     except cryptography.exceptions.InvalidTag:
         raise ProtectionInvalid("Tag invalid")
Exemple #19
0
 def encrypt(cls, plaintext, aad, key, iv):
     return aead.AESGCM(key).encrypt(iv, plaintext, aad)
Exemple #20
0
def get_aead_cipher(key, method):
    # method should be AEAD method
    if method.startswith('aes'):
        return aead.AESGCM(key)
    return SodiumAeadCrypto(method, key)
Exemple #21
0
def get_aead_cipher(key, method):
    '''get_aead_cipher
       method should be AEAD method'''
    if method.startswith('aes'):
        return aead.AESGCM(key)
    return aead.ChaCha20Poly1305(key)
Exemple #22
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)