Introdução e Contexto Histórico
A criptografia simétrica é o alicerce da proteção de dados em sistemas modernos. Diferentemente da criptografia assimétrica, que usa dois pares de chaves (pública e privada), a criptografia simétrica utiliza uma única chave compartilhada entre o remetente e o destinatário. Essa abordagem oferece desempenho superior, tornando-a ideal para criptografar grandes volumes de dados em tempo real.
O AES (Advanced Encryption Standard), padronizado em 2001, tornou-se a escolha padrão da indústria para criptografia governamental e comercial. Mais recentemente, o ChaCha20 emergiu como uma alternativa moderna, especialmente valiosa em contextos onde a resistência a ataques side-channel e o desempenho em dispositivos sem aceleração de hardware são críticos. Nesta aula, você compreenderá como esses algoritmos funcionam, como escolher entre eles e como implementá-los corretamente em produção.
AES (Advanced Encryption Standard)
Fundamentos e Operação
O AES é um algoritmo de cifra de bloco que processa dados em blocos de 128 bits, utilizando chaves de 128, 192 ou 256 bits. Sua segurança reside em operações matemáticas sofisticadas realizadas em múltiplas rodadas: SubBytes (substitição não-linear), ShiftRows (permutação), MixColumns (multiplicação matricial em GF(2⁸)) e AddRoundKey (XOR com a chave expandida).
A expansão de chave do AES gera chaves de rodada derivadas da chave mestra através de operações que envolvem rotação, substituição e XOR. Para uma chave de 256 bits, são criadas 15 chaves de rodada (14 rodadas mais a rodada inicial). Essa profundidade matemática garante que nem mesmo a recuperação parcial da chave de rodada comprometa significativamente a segurança do algoritmo.
Exemplo Prático com Python
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
# Geração de chave e IV (Initialization Vector)
chave = os.urandom(32) # 256 bits
iv = os.urandom(16) # 128 bits
# Dados a serem criptografados
mensagem = b"Informacao confidencial que precisa ser protegida"
# Criação do cipher AES em modo CBC
cipher = Cipher(
algorithms.AES(chave),
modes.CBC(iv),
backend=default_backend()
)
# Criptografia
encryptor = cipher.encryptor()
criptograma = encryptor.update(mensagem) + encryptor.finalize()
print(f"Chave (hex): {chave.hex()}")
print(f"IV (hex): {iv.hex()}")
print(f"Criptograma (hex): {criptograma.hex()}")
# Descriptografia
decipher = Cipher(
algorithms.AES(chave),
modes.CBC(iv),
backend=default_backend()
)
decryptor = decipher.decryptor()
texto_descriptografado = decryptor.update(criptograma) + decryptor.finalize()
print(f"Texto original: {texto_descriptografado.decode()}")
Este exemplo demonstra o fluxo básico: gerar uma chave criptograficamente segura, usar um IV único para cada mensagem (em modos que o exigem) e alternar entre operações de criptografia e descriptografia. A biblioteca cryptography abstrai a complexidade matemática, permitindo uso seguro sem expor detalhes perigosos.
ChaCha20 e Poly1305
Características e Vantagens
ChaCha20 é um algoritmo de fluxo (stream cipher) desenvolvido por Daniel Bernstein, projetado para superar limitações práticas do AES em ambientes heterogêneos. Enquanto o AES depende de aceleração por hardware (AES-NI em CPUs modernas) para desempenho ótimo, o ChaCha20 oferece performance consistente mesmo em processadores sem instruções especializadas. Além disso, ChaCha20 é intrinsecamente mais resistente a ataques side-channel baseados em timing, porque suas operações não dependem de tabelas de consulta com padrões de acesso variáveis.
Poly1305 é um autenticador universal de mensagens frequentemente combinado com ChaCha20 na construção ChaCha20-Poly1305, que fornece tanto confidencialidade quanto autenticidade em um único esquema (AEAD — Authenticated Encryption with Associated Data). Isso elimina a necessidade de combinar manualmente um algoritmo de criptografia com um código de autenticação, reduzindo a probabilidade de erros de implementação.
Exemplo Prático com Python
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from cryptography.hazmat.backends import default_backend
import os
# Geração de chave e nonce
chave = os.urandom(32) # ChaCha20 usa sempre 256 bits
nonce = os.urandom(12) # 96 bits para ChaCha20-Poly1305
dados_aad = b"header_ou_contexto" # Dados autenticados mas não criptografados
# Mensagem a criptografar
mensagem = b"Dados sensíveis que precisam de confidencialidade e autenticidade"
# Criptografia com autenticação
cipher = ChaCha20Poly1305(chave)
criptograma = cipher.encrypt(nonce, mensagem, dados_aad)
print(f"Chave (hex): {chave.hex()}")
print(f"Nonce (hex): {nonce.hex()}")
print(f"Criptograma + Tag (hex): {criptograma.hex()}")
# Descriptografia com verificação de autenticidade
try:
texto_descriptografado = cipher.decrypt(nonce, criptograma, dados_aad)
print(f"Texto original: {texto_descriptografado.decode()}")
except Exception as e:
print(f"Erro de autenticação: {e}")
# Simulação de adulteração
criptograma_adulterado = bytearray(criptograma)
criptograma_adulterado[0] ^= 1 # Flip um bit
try:
cipher.decrypt(nonce, bytes(criptograma_adulterado), dados_aad)
except Exception as e:
print(f"Detecção de adulteração: {type(e).__name__}")
Observe que ChaCha20-Poly1305 retorna um único bloco contendo o criptograma concatenado com a tag de autenticação (typically 16 bytes). A descriptografia valida a tag automaticamente, lançando uma exceção se houver manipulação.
Modos de Operação
ECB, CBC, CTR e GCM
Os modos de operação definem como um algoritmo de cifra de bloco processa dados maiores que o tamanho do bloco (128 bits para AES). O modo ECB (Electronic Codebook) é o mais simples: cada bloco é criptografado independentemente. Nunca use ECB em produção — blocos idênticos produzem criptogramas idênticos, vazando padrões.
O modo CBC (Cipher Block Chaining) XOR cada bloco de plaintext com o criptograma do bloco anterior, mascarando padrões. Requer um IV (Initialization Vector) único por mensagem. O modo CTR (Counter) transforma a cifra de bloco em um stream cipher, criptografando contadores sequenciais e XORando com o plaintext. CTR permite paralelização e acesso aleatório, diferentemente de CBC.
O modo GCM (Galois/Counter Mode) combina CTR com um código de autenticação universal eficiente, fornecendo AEAD como ChaCha20-Poly1305. GCM é a escolha padrão para AES em aplicações modernas que requerem autenticação.
Exemplo Prático: Comparação de Modos
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.backends import default_backend
import os
chave = os.urandom(32)
iv = os.urandom(16)
nonce_gcm = os.urandom(12)
mensagem = b"X" * 32 # 2 blocos AES
# Modo CBC
cipher_cbc = Cipher(algorithms.AES(chave), modes.CBC(iv), backend=default_backend())
encryptor = cipher_cbc.encryptor()
c1 = encryptor.update(mensagem) + encryptor.finalize()
print(f"CBC: {c1.hex()}")
# Modo CTR
cipher_ctr = Cipher(algorithms.AES(chave), modes.CTR(os.urandom(16)), backend=default_backend())
encryptor = cipher_ctr.encryptor()
c2 = encryptor.update(mensagem) + encryptor.finalize()
print(f"CTR: {c2.hex()}")
# Modo GCM (AEAD)
cipher_gcm = AESGCM(chave)
c3_com_tag = cipher_gcm.encrypt(nonce_gcm, mensagem, None)
print(f"GCM (com tag): {c3_com_tag.hex()}")
# Demonstração: ECB é ruim (não use!)
# cipher_ecb = Cipher(algorithms.AES(chave), modes.ECB(), backend=default_backend())
# encryptor = cipher_ecb.encryptor()
# c_ecb = encryptor.update(mensagem) + encryptor.finalize()
# # Blocos idênticos geram criptogramas idênticos!
# print(f"ECB (RUIM): {c_ecb.hex()}") # Padrão visível!
Repare que CBC necessita um IV único por mensagem, CTR permite paralelização e GCM fornece autenticação nativa. Cada modo tem trade-offs: CBC é amplamente suportado mas sequencial; CTR e GCM oferecem paralelização; GCM adiciona overhead de autenticação.
Casos de Uso e Boas Práticas
Seleção de Algoritmo e Modo
A escolha entre AES e ChaCha20 depende do contexto. Use AES-GCM quando: aceleração por hardware está disponível (servidor de data center), compatibilidade máxima é essencial ou requisitos regulatórios exigem AES. Use ChaCha20-Poly1305 quando: você opera em dispositivos móveis ou embarcados sem AES-NI, segurança side-channel é crítica ou você já utiliza bibliotecas modernas como libsodium.
IVs e nonces devem ser únicos para cada mensagem com a mesma chave. Para CBC, qualquer IV aleatório é aceitável. Para GCM e ChaCha20-Poly1305, use nonces monotônicos incrementados (recomendado) ou aleatórios de 96 bits. Nunca reutilize o mesmo nonce com a mesma chave — isso compromete toda a segurança do esquema.
Exemplo Prático: Sistema de Criptografia em Produção
import json
import base64
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.backends import default_backend
import os
import struct
class CryptoManager:
"""Gerenciador seguro de criptografia para produção."""
def __init__(self, chave_mestra: bytes):
if len(chave_mestra) != 32:
raise ValueError("Chave deve ter 256 bits")
self.chave = chave_mestra
def criptografar(self, dados: dict, aad: bytes = b"") -> str:
"""Criptografa e serializa dados com autenticação."""
nonce = os.urandom(12)
cipher = AESGCM(self.chave)
plaintext = json.dumps(dados).encode()
criptograma = cipher.encrypt(nonce, plaintext, aad)
# Formato: nonce(12) + criptograma+tag
payload = nonce + criptograma
return base64.b64encode(payload).decode()
def descriptografar(self, token: str, aad: bytes = b"") -> dict:
"""Descriptografa e deserializa, verificando autenticidade."""
payload = base64.b64decode(token)
if len(payload) < 12:
raise ValueError("Token inválido")
nonce = payload[:12]
criptograma = payload[12:]
cipher = AESGCM(self.chave)
plaintext = cipher.decrypt(nonce, criptograma, aad)
return json.loads(plaintext.decode())
# Uso
chave = os.urandom(32)
manager = CryptoManager(chave)
dados_usuario = {
"user_id": 42,
"email": "usuario@example.com",
"timestamp": 1234567890
}
token = manager.criptografar(dados_usuario, aad=b"user_session")
print(f"Token: {token}")
# Descriptografia com verificação
recuperados = manager.descriptografar(token, aad=b"user_session")
print(f"Dados recuperados: {recuperados}")
# Tentativa de adulteração detectada
token_adulterado = token[:-4] + "XXXX"
try:
manager.descriptografar(token_adulterado, aad=b"user_session")
except Exception as e:
print(f"Adulteração detectada: {type(e).__name__}")
Este exemplo encapsula criptografia segura em uma classe reutilizável: gera nonces únicos, usa GCM para autenticação, serializa dados JSON com segurança e detecta adulteração automaticamente.
Diretrizes de Segurança
- Nunca reutilize nonces com a mesma chave. Use contadores incrementados ou gere aleatoriamente com tamanho adequado (96 bits para GCM).
- Armazene chaves com segurança. Em produção, use Hardware Security Modules (HSMs), Key Management Services (KMS na AWS/GCP) ou cofres de chaves. Nunca hardcode chaves.
- Valide autenticação sempre. Se usar um modo AEAD, verifique a tag. Se usar um stream cipher com autenticação separada, calcule e valide HMACs.
- Use dados aleatórios criptograficamente seguros.
os.urandom()em Python 3,/dev/urandomem Unix ou APIs nativas de sistemas operacionais. - Acompanhe descobertas de segurança. AES ainda é seguro contra ataques teóricos conhecidos, mas ChaCha20 é uma alternativa comprovada se você tem capacidade de manutenção.
Conclusão
Você aprendeu que criptografia simétrica moderna depende de três elementos: algoritmos robustos (AES ou ChaCha20), modos de operação corretos (GCM ou ChaCha20-Poly1305para AEAD, CTR para paralelização) e implementação disciplinada (nonces únicos, armazenamento seguro de chaves, validação de autenticação).
A escolha entre AES e ChaCha20 não é absoluta — AES-GCM domina em data centers com aceleração de hardware, enquanto ChaCha20-Poly1305 brilha em dispositivos móveis e contextos side-channel sensíveis. Ambos são criptograficamente sólidos quando implementados corretamente.
Finalmente, a segurança de um sistema é tão forte quanto seu elo mais fraco: um IV reutilizado ou uma chave em plaintext anula toda a sofisticação matemática dos algoritmos. Priorize integração com bibliotecas auditadas (libsodium, cryptography do Python), validação rigorosa de autenticação e monitoramento contínuo de práticas recomendadas.
Referências
- NIST Special Publication 800-38A: Recommendation for Block Cipher Modes of Operation
- ChaCha20 and Poly1305 for IETF Protocols (RFC 8439)
- PyCA cryptography Documentation
- Serious Cryptography: A Practical Introduction to Modern Encryption by Jean-Philippe Aumasson
- Bernstein, D. J. "ChaCha, a variant of Salsa20"