O paradigma KEM-DEM é um tipo de protocolo criptográfico que permite o uso de criptotextos de tamanhos arbitrários para a geração de chaves de tamanho fixo.
Uma vez que o RSA é um sistema bastante lento, é muito comum o uso da chamada “criptografia híbrida”, onde um sistema assimétrico (como o RSA) é usado para compartilhar a chave de um sistema simétrico (como o AES), e esse sistema simétrico é usado daí em diante.
Apesar disso, existe um problema inerente de compatibilidade entre os dois sistemas usados, pois muitas vezes o tamanho ideal de mensagem para o sistema assimétrico não bate com o tamanho de chave do sistema assimétrico. No caso da dupla RSA-AES, isso ocorre bastante: o tamanho de mensagem que torna o RSA seguro é aproximadamente o mesmo tamanho do módulo, 256 bytes ou 384 bytes, dependendo da implementação, enquanto a chave do AES tem no máximo 32 bytes.
Isso implica que, para que o sistema seja seguro, devemos expandir a chave de alguma maneira, usando algum tipo de padding. O problema é que padding na criptografia é algo muito perigoso, que quase sempre introduz vulnerabilidade no sistema, vide o ataque Bleichenbacher. Uma solução melhor é adaptar o próprio paradigma de geração de chaves: geramos uma mensagem aleatória (de forma segura) de tamanho ideal para o RSA e a partir dessa mensagem aleatória grande, derivamos a chave, por exemplo usando uma função hash.
Esse é o chamado KEM (Key Encapsulation Mechanism). Quando um KEM é usado para se trocar mensagens, dizemos que o sistema é um DEM (Data Encapsulation Mechanism). É possível provar que esse mecanismo como um todo é tão seguro quanto suas partes (as implementações do RSA, AES, do hash e do PRNG) e, portanto, não introduz vulnerabilidades.
def rsa_encapsulate(public_key):
= gen_random_bytes(256)
random = RSA.enc(random, public_key)
enc_random = sha256(random) # 256 bits = 32 bytes!
aes_key
return enc_random, aes_key
def rsa_decapsulate(enc_random, private_key):
= RSA.dec(enc_random, private_key)
random = sha256(random)
aes_key
return aes_key