-O2 elimina completamente las llamadas memset() sobre buffers de claves AES-256 y tokens JWT. Los secretos permanecen en pila y heap accesibles tras la liberación del scope. La certificación PCI-DSS DSS 3.2.1 Req. 3.5 no puede cumplirse hasta resolver ZA-0001, ZA-0003 y ZA-0005.
| ID | Nombre | Tipo | Archivo | Línea | Zeroizado | Hallazgo |
|---|---|---|---|---|---|---|
| SO-0001 | aes_key_buf | crypto_key | crypto_engine.c | 142 | ✗ DSE eliminado | ZA-0001 |
| SO-0002 | rsa_private_key | crypto_key | crypto_engine.c | 218 | ~ parcial (2048/4096 bytes) | ZA-0002 |
| SO-0003 | session_password | credential | auth_session.c | 89 | ✗ ausente | ZA-0003 |
| SO-0004 | jwt_secret | credential | auth_session.c | 156 | ✗ DSE eliminado | ZA-0005 |
| SO-0005 | ecdh_ephemeral | crypto_key | tls_wrapper.c | 312 | ~ solo en ruta happy-path | ZA-0004 |
| SO-0006 | master_key | crypto_key | key_manager.c | 67 | ~ heap sin secure_malloc | ZA-0006 |
| SO-0007 | derived_key | crypto_key | key_manager.c | 134 | ✓ explicit_bzero | — |
| SO-0008 | hmac_key | crypto_key | crypto_engine.c | 287 | ✓ explicit_bzero | — |
| SO-0009 | tls_session_key | crypto_key | tls_wrapper.c | 198 | ~ copias no rastreadas | ZA-0007 |
| SO-0010 | pin_buffer | credential | auth_session.c | 203 | ? necesita revisión | ZA-0008 |
| SO-0011 | nonce_buf | nonce | crypto_engine.c | 341 | ? loop parcial (14/16 bytes) | ZA-0009 |
| SO-0012 | api_token_cache | credential | key_manager.c | 412 | ✓ memset_s (C11) | — |
memset() por explicit_bzero(aes_key_buf, 32) o añadir barrera de compilador: asm volatile("" ::: "memory") tras el memset.strdup(). El allocator estándar (malloc/free) no borra el contenido. Los bytes de la contraseña permanecen en el chunk del heap hasta ser sobreescritos por una asignación futura — típicamente accesibles hasta minutos después del free(). Además, el objeto vc_session_t también se libera sin limpiar (SO-0003 secundario).
master_key (key_manager.c:67) también usa malloc() estándar en lugar de un allocator seguro. Véase ZA-0006.
explicit_bzero(s->password, strlen(s->password)) inmediatamente antes de free(s->password). Considerar sodium_malloc/sodium_free para allocations de credenciales.explicit_bzero(jwt_secret, 64) en línea 154. Como alternativa: OPENSSL_cleanse(jwt_secret, 64) si OpenSSL ya es dependencia del proyecto.memset(rsa_private_key, 0, 256) pero el buffer es uint8_t rsa_private_key[512] (4096 bits = 512 bytes). Solo se limpian los primeros 256 bytes; los 256 finales contienen los factores p, q de la clave RSA.explicit_bzero(rsa_private_key, sizeof(rsa_private_key)) para garantizar el tamaño completo independientemente del tipo.HANDSHAKE_TIMEOUT, CERT_VERIFY_FAIL, ALERT_RECEIVED) que saltan al label cleanup_partial sin pasar por el bloque de zeroización de ecdh_ephemeral.explicit_bzero(ecdh_ephemeral, sizeof(ecdh_ephemeral)) al label cleanup_all que domina todos los exits, o usar el patrón goto cleanup unificado.master_key = malloc(KEY_SIZE). El allocator estándar no garantiza borrado al liberar. En sistemas con swap, la clave puede persistir en disco. Recomendado: sodium_malloc + sodium_free que activan mlock() y limpieza segura.tls_session_key se copia a export_keying_material_buf (línea 211) y luego al parámetro de salida (línea 215). Solo la copia raíz tiene zeroización; las dos copias derivadas no. Confianza reducida a needs_review (MCP Serena no disponible para cross-reference).vc_validate_pin() retorna VC_ERR_INVALID_PIN antes de alcanzar el bloque de zeroización de pin_buffer cuando el primer dígito es incorrecto (early-exit). Solo confirmable con CFG completo; downgradeado a needs_review sin Serena MCP.
memset(nonce_buf, 0, AES_NONCE_SIZE - 2). La constante AES_NONCE_SIZE vale 16 pero se restan 2, dejando los últimos 2 bytes del nonce sin limpiar. Aunque un nonce expuesto es menos grave que una clave, puede facilitar ataques de reutilización de nonce en AES-GCM.
| Hallazgo | Archivo PoC | Compilar con | Resultado | Verificado |
|---|---|---|---|---|
| ZA-0001 | poc/ZA-0001.c | gcc -O2 | EXIT 0 — exploitable | ✓ verificado |
| ZA-0003 | poc/ZA-0003.c | gcc -O2 | EXIT 0 — exploitable | ✓ verificado |
| ZA-0005 | poc/ZA-0005.c | gcc -O2 | EXIT 0 — exploitable | ✓ verificado |
| ZA-0002 | poc/ZA-0002.c | gcc -O1 | EXIT 0 — exploitable | ✓ verificado |
| ZA-0004 | poc/ZA-0004.c | gcc -O0 | EXIT 0 — exploitable | ✓ verificado |
| ZA-0006 | poc/ZA-0006.c | gcc -O2 | EXIT 1 — no exploitable | ✓ verificado |
| ZA-0007 | poc/ZA-0007.c | gcc -O2 | EXIT 0 — exploitable | ✓ verificado |
| ZA-0008 | poc/ZA-0008.c | gcc -O0 | COMPILE FAIL (parcial) | — no compila |
| ZA-0009 | poc/ZA-0009.c | gcc -O2 | EXIT 0 — exploitable | ✓ verificado |
explicit_bzero en vc_aes_encrypt()explicit_bzero antes de free(session_password)explicit_bzero en vc_sign_jwt()
memset(rsa_private_key)sodium_malloctls_session_key
nonce_buf wipe
explicit_bzero como estándar del proyecto-fsanitize=memory en CI para detectar DSEsodium_malloc para todas las credenciales heap