AudioTrust: reconciliar C2PA y watermark AudioSeal en audio sintético
Un verificador local que lee las dos marcas de confianza de un audio generado por IA (procedencia C2PA + watermark AudioSeal) y emite un veredicto auditable sobre si coinciden, se contradicen o faltan.
El problema
Un audio sintético puede llevar dos marcas de confianza distintas:
- Procedencia C2PA: un certificado digital embebido en el archivo (su “DNI” de origen — quién, cuándo, con qué herramienta).
- Watermark AudioSeal: un código inaudible incrustado en el sonido, detectable aunque el audio se comparta o transcodifique.
Cada una por separado es útil, pero ninguna es suficiente. La procedencia puede faltar (mucho audio generado no la incluye) y el watermark puede estar presente en audio totalmente legítimo. El caso interesante es cuando se contradicen: el manifest C2PA dice “grabado por un humano con una grabadora” pero el watermark de una herramienta de IA está presente. Eso es una señal de manipulación — el llamado Integrity Clash.
AudioTrust no genera ni firma nada. Es un verificador: lee ambas capas y las reconcilia.
Qué hace
audio.wav ──► AudioTrust verify ──► veredicto + explicación
Enter fullscreen mode Exit fullscreen mode
C2PA watermark Veredicto ausente ausenteunverifiable
ausente
presente
partial
origen sintético
presente
trusted
origen humano
presente
contradiction (Integrity Clash)
Salida JSON:
{
"file": "audio.wav",
"verdict": "trusted",
"c2pa": {"present": true, "source_type": null, "claims": ["action=c2pa.created by TestTTS", "generatedBy=TestTTS"]},
"watermark": {"present": true, "detect_prob": 0.92},
"explanation": "C2PA declara origen sintético y hay watermark fuerte: coherentes."
}
Enter fullscreen mode Exit fullscreen mode
Cómo funciona
-
Lectura C2PA con
c2pa-python(el Reader de la librería oficial). Si no hay manifest, devuelvepresent=Falsesin crashear. -
Detección de watermark con
audioseal. Devuelve solodetect_prob(P(audio watermarked) en [0,1]). -
Reconciliación determinista en
reconcile.py.
Dos decisiones de diseño que vale la pena explicitar porque no son obvias:
-
Nunca se usa el mensaje decodificado de AudioSeal. En el spike de validación, el mensaje recuperado fue inconsistente entre corridas (la primera coincidía con el original, las siguientes no) pese a un
detect_probestable. Por tanto el producto se apoya solo endetect_prob, que sí es reproducible. El mensaje se descarta explícitamente en el código. -
El origen se deduce de los claims de acción del manifest, no del campo
source_type. Verificamos con firmas reales dec2patoolque los manifests C2PA reales no incluyensource_typeexplícito por defecto (usanc2pa.actionsconc2pa.created). Clasificar como “sintético” solo por la presencia dec2pa.createdes un error: esa acción está en casi cualquier manifest, sea IA o humano.
El bug que casi se cuela
La lógica de reconciliación original clasificaba como synthetic cualquier claim con c2pa.created genérico, salvo que el nombre del agente contuviera literalmente “human”/”camera”/”capture”. Con un agente humano realista —”Zoom H4n”, “Voice Memos”— el origen caía en synthetic, y si el detect_prob daba alto, el veredicto era trusted en vez de contradiction.
Eso invertía el propósito del producto: trusted demasiado fácil, contradiction (el caso que justifica la herramienta) casi inalcanzable.
La corrección: el default sin evidencia positiva de origen es indeterminate (→ partial), nunca synthetic. “Sintético” exige una señal explícita de generación por IA (generatedBy, softwareAgent, trained_algorithmic…); “humano” exige señal de captura. Así trusted se gana con evidencia y contradiction es alcanzable.
Este bug lo encontró la auditoría independiente en un clone limpio, no el self-report de quien lo escribió. Se añadió un test con el caso “Zoom H4n” para que no vuelva a colarse.
El proceso (por qué confío en el resultado)
El repositorio no se escribió de una sentada. Siguió gobernanza de tres partes:
-
Spike de viabilidad (¿
c2pa-pythonyaudiosealfuncionan de verdad en local? — sí, verificado con 3 corridas en 2 entornos distintos). - SDD: Constitución → Spec → Tasks.
-
Implement en rama dedicada
feature/audiotrust(nuncamain). - Auditoría externa en clone limpio: encontró el bug de reconciliación arriba.
-
Corrección + re-verificación, luego merge a
maincon aprobación explícita.
En cada paso donde apareció un problema real —un falso positivo de watermark en audio limpio, el bug de reconciliación, la limitación de c2patool con WAV— quedó documentado con evidencia cruda en KNOWN_ISSUES.md, no escondido bajo tests verdes.
Lo que queda abierto (honestamente)
El caso de uso estrella —trusted/contradiction con un archivo de audio real firmado— hoy solo está cubierto por tests unitarios con el manifest mockeado. No hay un fixture de audio con manifest C2PA real que dispare esos dos veredictos end-to-end, porque c2patool 0.9.12 firma imágenes JPG pero no acepta WAV en este entorno (limitación de la build, no del binario). Es trabajo de maduración post-merge, documentado como KI-5, y no bloquea el MVP: partial y unverifiable sí están probados con datos 100% reales (watermark AudioSeal real + WAV sin manifest).
Pruébalo
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
export TORCH_COMPILE_DISABLE=1 TORCHDYNAMO_DISABLE=1 # requerido en CPU
audiotrust verify ruta/al/audio.wav --json
Enter fullscreen mode Exit fullscreen mode
Los pesos de AudioSeal se descargan en la primera ejecución (repo público, sin token).
Enlaces
- Repo: https://github.com/amurlaniakea/audiotrust
- Licencia: AGPL-3.0-or-later — Pedro Sordo Martínez
답글 남기기