Hybrid KEM-DEM
AegisQ implementa una arquitectura Hybrid KEM-DEM. ML-KEM no puede cifrar payloads grandes directamente — solo produce un shared secret de 32 bytes. AegisQ lo combina con AES-256-GCM como mecanismo de encapsulación de datos (DEM).
El Enfoque Híbrido
Sección titulada «El Enfoque Híbrido»- ML-KEM (KEM): Genera un shared secret de 32 bytes, resistente a quantum
- AES-256-GCM (DEM): Usa ese secret de 32 bytes como clave simétrica para cifrar el payload real con cifrado autenticado (confidencialidad + integridad)
Propiedades de AES-256-GCM
Sección titulada «Propiedades de AES-256-GCM»| Propiedad | Valor |
|---|---|
| Tamaño de clave | 256 bits (32 bytes) — del shared secret de ML-KEM |
| Nonce (IV) | 96 bits (12 bytes) — aleatorio por operación vía OsRng |
| Authentication Tag | 128 bits (16 bytes) |
| Seguridad | IND-CPA + INT-CTXT (cifrado autenticado) |
Una vez que ML-KEM genera el shared secret K de 32 bytes, AegisQ lo alimenta directamente a AES-256-GCM como clave de cifrado simétrico. No se necesita un KDF adicional — la salida de 32 bytes de ML-KEM ya es uniformemente aleatoria y del tamaño correcto para AES-256.
Manejo del Nonce — Regla Crítica
Sección titulada «Manejo del Nonce — Regla Crítica»Ensamblado del Transit Package
Sección titulada «Ensamblado del Transit Package»El módulo hybrid.rs en aegisq-core es responsable de ensamblar y parsear el Transit Package.
Estructura del Transit Package
Sección titulada «Estructura del Transit Package»El array de bytes final encrypted_package tiene esta estructura fija:
[ ML-KEM Capsule (var) | AES Nonce (12 bytes) | AES Auth Tag (16 bytes) | Ciphertext (var) ]Donde el tamaño de ML-KEM Capsule depende del nivel de seguridad:
- ML-KEM-512: 768 bytes
- ML-KEM-768: 1088 bytes
- ML-KEM-1024: 1568 bytes
Flujo de Cifrado
Sección titulada «Flujo de Cifrado»- Llamar
kem::encapsulate(public_key, level)→ resultado con capsule y shared secret - Generar nonce aleatorio de 12 bytes vía
OsRng - Llamar
aes_gcm::encrypt(key=shared_secret, nonce, plaintext)→(tag, ciphertext) - Zeroizar
shared_secretinmediatamente - Ensamblar y retornar:
capsule || nonce || tag || ciphertext
Flujo de Descifrado
Sección titulada «Flujo de Descifrado»- Dividir el Transit Package por offsets conocidos (capsule_size, luego 12, 16, resto)
- Llamar
kem::decapsulate(capsule, secret_key, level)→shared_secret_32B - Llamar
aes_gcm::decrypt(key=shared_secret, nonce, tag, ciphertext)→plaintextoErr - Zeroizar
shared_secretinmediatamente - Si la verificación del tag falla → retornar
Err(AegisQError::DecryptionFailed)
Contraste del Comportamiento de Errores
Sección titulada «Contraste del Comportamiento de Errores»| Escenario | ML-KEM Decaps | AES-GCM Decrypt |
|---|---|---|
| Contenido inválido, tamaño de capsule correcto | Retorna K pseudoaleatoria | Se espera que falle la verificación del tag |
| Tamaño incorrecto de clave/capsule o paquete demasiado corto | Error estructural (InvalidParameterError) | No se alcanza |
| Capsule correcta, clave AES incorrecta | N/A (la clave se deriva de la capsule) | Error: DecryptionError |
| Auth tag no coincide (payload manipulado) | N/A | Error: DecryptionError |
| Todo correcto | Retorna shared secret | Retorna plaintext |
API Interna de Rust
Sección titulada «API Interna de Rust»use aegisq_core::{hybrid, kem::SecurityLevel};
// Hybrid encrypt: ML-KEM encaps + AES-256-GCMlet encrypted_package: Vec<u8> = hybrid::encrypt( recipient_public_key, plaintext, SecurityLevel::MlKem768,)?;
// Hybrid decrypt: ML-KEM decaps + AES-256-GCM verify + decryptlet plaintext: Vec<u8> = hybrid::decrypt( &encrypted_package, secret_key, SecurityLevel::MlKem768,)?;