Criptografía en Python - Cifrado Híbrido


Esquema de cifrado híbrido
El cifrado híbrido no es más que una combinación con cierta elegencia del ya
conocido
cifrado simétrico
y el amado
cifrado asimétrico. Los dos se complementan para no solo por ser eficiente, sino que también
superar las limitaciones que impone cada uno. Mientras que la limitación del
cifrado simétrico es compartir la clave secreta que no puede ser revelada por
un tercero, la del asimétrico es el bajo rendimiento que ofrece, el increíble
poder de computo que requiere mas el no poder cifrar menos que el tamaño de
clave generado.

Como ya se habló sobre que no es una solución inteligente el aumentar el
tamaño de clave en un algoritmo de cifrado asimétrico, ya que aunque en teoría
sería más seguro, se debe buscar un equilibrio entre eficiencia y la seguridad
según el poder de computo en la actualidad.
Esta no es la primera vez que se hablado sobre este método para compartir
datos de forma segura, ya se hizo con
GnuPG
hablando sobre un tema importante en la sociedad que muchos dan por hecho o
ignoran porque simplemente creen que así se solucionaría todo. La diferencia
sustancial entre ese artículo y el actual, es que el anterior se usa GnuPG
abstrayendo un poco al programador en la práctica. Es cierto, hay que tener
cierta base para comprender y usar eficazmente GnuPG, pero no es tanta como se
pensaría, así que este artículo va más allá de lo que se ha enseñado, también
será complementarío y quizás un repaso a lo que ya hemos observado a lo largo
de esta serie de artículos meramente entretenidos y de índole educativa.
Índice()

    Creando lo híbrido en lo cifrado

    Un título sin sentido, simplón e irregular, pero una vez que experimentemos
    cómo crearlo verán que comienza a agradar y a incluir un significado
    coherente en nuestra mente.
    En una aplicación o un nombre mucho más bonito, un proyecto, se requiere
    separar cada pieza para tratarla de diferentes maneras, podría llamarse
    modularidad, pero básicamente lo que se trata de hacer es resolver un
    problema diferente para cada parte que lo compone. Trataremos de hacer
    exactamente lo propuesto anteriormente, separar en tres archivos
    (aes_eax.py, rsa.py, hibrid.py) con objetivo de tratarlos de diferente
    manera y poder lograr el esquema del susodicho cifrado híbrido.
    Comencemos con aes_eax.py:
    from Crypto.Cipher import AES
    
    def encrypt(key, data):
        cipher = AES.new(key, AES.MODE_EAX)
        ciphertext, tag = cipher.encrypt_and_digest(data)
    
        return cipher.nonce + tag + ciphertext
    
    def decrypt(key, data):
        nonce = data[:AES.block_size]
        tag = data[AES.block_size:AES.block_size * 2]
        ciphertext = data[AES.block_size * 2:]
    
        cipher = AES.new(key, AES.MODE_EAX, nonce)
        
        return cipher.decrypt_and_verify(ciphertext, tag)
    

    El código anterior no es para nada novedoso, ya se ha descrito en el
    capítulo dedicado especialmente a AES.
    Ahora lo otro que necesitamos es rsa.py:
    from Crypto.Cipher import PKCS1_OAEP
    from Crypto.PublicKey import RSA
    
    def generate(bit_size):
        keys = RSA.generate(bit_size)
    
        return (keys, keys.publickey())
    
    def encrypt(pub_key, data):
        cipher = PKCS1_OAEP.new(pub_key)
    
        return cipher.encrypt(data)
    
    def decrypt(priv_key, data):
        cipher = PKCS1_OAEP.new(priv_key)
    
        return cipher.decrypt(data)

    No hay nada especial con lo mostrado anteriormente, es como repasar cosas
    que ya hemos aprendido en el pasado pero que seguiremos usando cuantas veces
    sean necesarias.
    Muy bien, hemos dado vida a lo principal y posiblemente lo más complejo de
    todo, ahora hace falta lo que me motivó a escribir este pequeño escrito,
    hibrid.py:
    import os
    
    import aes_eax
    import rsa
    
    key_size = 32
    
    def encrypt(pub_key, data):
        session_key = os.urandom(key_size)
        enc_key = rsa.encrypt(pub_key, session_key)
        enc_data = aes_eax.encrypt(session_key, data)
    
        return enc_key + enc_data
    
    def decrypt(priv_key, data):
        key_size = priv_key.size_in_bytes()
        enc_key = data[:key_size]
        enc_data = data[key_size:]
    
        dec_key = rsa.decrypt(priv_key, enc_key)
        dec_data = aes_eax.decrypt(dec_key, enc_data)
    
        return dec_data
      

    La constante key_size se especifica para describir el tamaño en bytes de la clave que será generada aleatoriamente por os.urandom(...), luego se encriptará la contraseña generada usando RSA rompiendo el problema de compartirla en un canal inseguro, y por último usamos AES para encriptar los datos rápidamente.
    Ahora en la función decrypt(...) tendremos que calcular los datos del emisor. Como había dicho en anteriores partes, RSA no cifra más allá del tamaño de la clave, así que usamos la función .size_in_bytes(...) para calcular ese tamaño en bytes y parsear correctamente tanto la clave que está encriptada como los datos que igualmente están encriptados. A posteriori desencriptamos la clave y los datos para retornarlos efectivamente.
    Si se observa minuciosamente, el título sí tiene sentido cuando se analiza correctamente el código.

    De la teoría a la práctica

    Ya sabemos tanto conceptos como el código que hay que usar, pero no lo hemos puesto en práctica. Pues no hace falta más que ejecutar la consola de python, importar un par de módulos y comenzar a jugar. Empecemos:
    Ejecución de toda la sopa de código que hemos creado

    Lecturas recomendadas

    • https://pycryptodome.readthedocs.io/en/latest/src/examples.html#encrypt-data-with-rsa
    • https://es.wikipedia.org/wiki/Criptograf%C3%ADa_h%C3%ADbrida
    • https://www.gnupg.org/gph/es/manual.html
    • http://www.pythondiario.com/2019/07/tutorial-como-enviar-correos-con-el.html
    ~ DtxdF

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

    Subir

    Te has suscrito correctamente al boletín

    Se produjo un error al intentar enviar tu solicitud. Inténtalo de nuevo.

    Mi Diario Python will use the information you provide on this form to be in touch with you and to provide updates and marketing.