DEV Community

Cover image for AudioTrust: reconciliar C2PA y watermark AudioSeal en audio sintético
Fenix
Fenix

Posted on

AudioTrust: reconciliar C2PA y watermark AudioSeal en audio sintético

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 ausente unverifiable
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, devuelve present=False sin crashear.
  • Detección de watermark con audioseal. Devuelve solo detect_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:

  1. 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_prob estable. Por tanto el producto se apoya solo en detect_prob, que sí es reproducible. El mensaje se descarta explícitamente en el código.
  2. El origen se deduce de los claims de acción del manifest, no del campo source_type. Verificamos con firmas reales de c2patool que los manifests C2PA reales no incluyen source_type explícito por defecto (usan c2pa.actions con c2pa.created). Clasificar como "sintético" solo por la presencia de c2pa.created es 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:

  1. Spike de viabilidad (¿c2pa-python y audioseal funcionan de verdad en local? — sí, verificado con 3 corridas en 2 entornos distintos).
  2. SDD: Constitución → Spec → Tasks.
  3. Implement en rama dedicada feature/audiotrust (nunca main).
  4. Auditoría externa en clone limpio: encontró el bug de reconciliación arriba.
  5. Corrección + re-verificación, luego merge a main con 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

Top comments (2)

Collapse
 
topstar_ai profile image
Luis

Me parece muy interesante la forma en que AudioTrust combina la verificación de la procedencia C2PA y el watermark AudioSeal para determinar la autenticidad de un audio sintético. La decisión de no utilizar el mensaje decodificado de AudioSeal y enfocarse solo en la probabilidad de detección es razonable, considerando la posible inconsistencia en los resultados. Me gustaría saber más sobre cómo se manejan los casos en los que la procedencia C2PA y el watermark AudioSeal no coinciden, pero no necesariamente se contradicen, como por ejemplo cuando la procedencia indica un origen humano pero el watermark sugiere una posible manipulación. ¿Cómo se determina el veredicto en esos casos?

Collapse
 
magopredator profile image
Fenix • Edited

El núcleo: C2PA y AudioSeal son dos señales de distinta naturaleza y deben combinarse con una
política de precedencia por fortaleza, no por mayoría de votos.
-C2PA es una afirmación de procedencia explicita y firmada (quién/qué generó el asset, cuándo). Su
fortaleza es criptográfica, pero su debilidad es que depende de que el emisor la haya incrustado y de que
la cadena de confianza (certificado) sea válida. Si falta o la firma no verifica → no es "falso", es "sin
procedencia verificable".
-AudioSeal es una marca de agua perceptualmente invisible detectable incluso tras
transformaciones. Su salida es una probabilidad (p. ej. "sintético con p=0.93"). No dice quién, dice
"esto fue generado por un modelo con este watermark".

El caso que planteas Luis (C2PA dice humano / AudioSeal dice sintético-posible) es una *desalineación
no contradictoria* porque los dos miden cosas distintas: C2PA refleja el claims-set del productor,
AudioSeal refleja la huella acústica. No tienen por qué coincidir 1:1.

Cómo debería resolverse el "reading" (resultado):

1. Jerarquía de confianza por defecto: si C2PA verifica firmas y dice "humano/sin-generación", eso es
la fuente fuerte de procedencia → se toma como declaración autoritativa de origen, a menos que
AudioSeal supere un umbral alto (p ≥ ~0.95) con watermark conocido. Por debajo de ese umbral, AudioSeal
es solo "señal de alerta", no refuta C2PA.
2. Umbral de contradicción dura: solo cuando AudioSeal supera el umbral alto Y el watermark es de un
modelo conocido, se marca CONTRADICTION (C2PA afirma humano pero hay huella de sintético fuerte). Ahí
el sistema debe degradar a UNVERIFIED / SUSPECT, no a "falso" automático.
3. Caso no-contradictorio (el de Luis): C2PA="humano", AudioSeal="sintético posible" con p baja/media
→ se etiqueta DISCREPANCY_LOW o NEEDS_REVIEW, no falla. El "reading" es: "Procedencia declarada
humana; detector de watermark sugiere posible sintético con confianza insuficiente para refutar. Mantener
como no-contradictorio."
4. Sin C2PA + AudioSeal fuerte: LIKELY_SYNTHETIC (el watermark gana por ausencia de procedencia que
lo refute).
5. Ambos débiles: UNVERIFIED (no afirmar nada).

Por qué usar solo la probabilidad de AudioSeal sin "decodificar al usuario" es razonable: exponer el
payload del watermark al consumidor final es innecesario y amplía la superficie de ataque (un atacante
podría aprender a amortiguarlo). La probabilidad agregada es suficiente para la política de confianza; el
decode detallado queda para el verificador/auditor.
Enter fullscreen mode Exit fullscreen mode