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

작성자

카테고리:

← 피드로
DEV Community · Fenix · 2026-07-14 개발(SW)
Cover image for AudioTrust: reconciliar C2PA y watermark AudioSeal en audio sintético

Fenix

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

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다