Aller au contenu

Vérifier un fichier

POST https://api.kennmarq.com/v1/verify

GET https://api.kennmarq.com/v1/credentials/{watermark_id}

GET https://api.kennmarq.com/v1/verify/key

Ce que disent le manifeste C2PA et les filigranes invisibles d’une image, d’un morceau ou d’une vidéo, qu’ils viennent de nous ou de n’importe qui d’autre ; sur demande, sous forme de résultat signé par nous. Gratuit, sans clé. La page du vérificateur fait de même pour les images, dans votre navigateur, sans rien envoyer.

POST /v1/verify

Envoyez l’image en multipart/form-data dans file (PNG, JPEG ou WebP ; la requête fait au plus 4 Mo). Ajoutez signed=true pour obtenir un résultat signé. Nos serveurs de Zurich lisent le fichier en mémoire et ne conservent rien. Une image plus lourde, jusqu’à 25 Mo, passe par un envoi indiqué dans upload, ce qui nécessite la clé qui l’a créé ; ou vérifiez-la sur la page du vérificateur, dans votre navigateur, quelle que soit sa taille jusqu’à 25 Mo. Les morceaux (MP3, WAV, M4A) et les vidéos (MP4) sont lus de la même façon sur nos serveurs, jusqu’à 25 Mo, avec une clé (n’importe laquelle des vôtres, gratuitement ; rien n’est décompté) : voir morceaux et vidéos.

curl
curl https://api.kennmarq.com/v1/verify -F file=@picture.jpg -F signed=true
200 application/json
{
  "object": "check",
  "checked_at": "2026-11-12T10:00:00Z",
  "checker": { "name": "Kennmarq.com", "version": "0.4.0" },
  "file": { "type": "image/jpeg", "bytes": 482113, "width": 1024, "height": 768, "sha256": "…" },
  "c2pa": {
    "state": "Trusted",
    "manifest": "urn:c2pa:3fa85f64-…",
    "signer": { "common_name": "Kennmarq.com", "issuer": "Neuramare", "ours": true, "environment": "live" },
    "signed_at": "2026-11-12T09:58:31+00:00",
    "time_stamp": { "validated": true, "trusted": true },
    "claim_generator": "Kennmarq.com 0.4.0",
    "ai": { "digital_source_type": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia", "generated": true },
    "soft_binding": { "alg": "com.adobe.trustmark.Q", "value": "1*0101…" },
    "failures": []
  },
  "watermark": { "found": true, "alg": "com.adobe.trustmark.Q", "schema": "BCH_5", "id": "1d455a804e9802a6", "matches_manifest": true },
  "durable_credential": {
    "watermark_id": "1d455a804e9802a6",
    "manifest": "urn:c2pa:3fa85f64-…",
    "environment": "live",
    "asset_sha256": "…",
    "same_file": true,
    "log_index": 1834001,
    "url": "https://api.kennmarq.com/v1/credentials/1d455a804e9802a6"
  },
  "log": { "leaf_index": 1834001, "status": "included", "proof_url": "https://api.kennmarq.com/v1/log/proof/1834001" },
  "signed": { "jws": "eyJhbGciOiJFUzI1NiIs…", "key_id": "…", "log_index": 1834530 }
}
  • c2pa vaut null quand le fichier ne porte aucun manifeste. state vaut Trusted quand la signature remonte à nos racines (ours), Valid pour un autre signataire dont la signature est valide, et Invalid quand le fichier a été modifié après sa signature ; failures en liste les codes.
  • watermark.id est renseigné pour les filigranes de notre schéma (BCH_5) ; les autres filigranes TrustMark apparaissent comme trouvés, avec leur schéma, quand le centre de l’image donne exactement la même lecture, pour qu’une image sans marquage n’en affiche pas un par hasard. Le nôtre se lit aussi dans une image enregistrée sur le côté ou à l’envers.
  • durable_credential : ce à quoi le filigrane mène dans nos registres, quand c’est nous qui avons signé le fichier. Quand une plateforme supprime les métadonnées, c’est ainsi que l’on retrouve les Content Credentials ; same_file indique s’il s’agit exactement du fichier que nous avons signé, et vaut null pour un fichier marqué en mode local, qui ne nous est jamais parvenu.
  • log : l’entrée du manifeste dans notre journal de transparence.
  • Morceaux et vidéos : file comporte seconds (et, pour une vidéo, sa taille) ; watermarks comporte la lecture Video Seal de la vidéo (id, lu sur 64 images, et agreement, la part de ses 256 bits qui concordent) et celle d’AudioSeal (detected, la part du son lue comme marquée, et bits sur 16) ; c2pa ajoute claims, les filigranes que le fichier porte selon le manifeste, et, pour une vidéo, watermark_record. watermark est alors le Video Seal de la vidéo, qui mène à ses Content Credentials durables comme celui d’une image, ou l’AudioSeal du morceau, qui indique que le fichier a été marqué par nous mais ne porte aucun ID.

Résultats signés

Avec signed=true, signed.jws est un JWS (RFC 7515, compact) signé en ES256 par notre clé de vérificateur : sa charge utile est le résultat ci-dessus (le fichier, son manifeste et son filigrane, l’heure), plus iss, iat et un jti unique ; le kid de son en-tête désigne la clé. Avant d’être renvoyé, il est inscrit dans notre journal de transparence sous la forme d’une entrée checker.result (le SHA-256 du JWS, celui du fichier, et l’ID de la clé), à l’index indiqué dans X-Log-Index. N’importe quelle bibliothèque JWT le vérifie avec la clé fournie par GET /v1/verify/key.

Node.js (jose)
import { decodeProtectedHeader, importSPKI, jwtVerify } from "jose";

const { jws } = result.signed;
const { kid } = decodeProtectedHeader(jws);
const key = await (await fetch(`https://api.kennmarq.com/v1/verify/key?key_id=${kid}`)).json();
const { payload } = await jwtVerify(jws, await importSPKI(key.public_key, "ES256"), { issuer: "Kennmarq.com" });
// payload.file.sha256 is the SHA-256 of the file that was checked; compare it with yours.
console.log(payload.file.sha256, payload.checked_at);

GET /v1/credentials/{watermark_id}

Le magasin de manifestes C2PA auquel mène un filigrane, conservé depuis le moment où nous avons signé le fichier : en JSON (le magasin en base64 dans manifest_store), ou avec ?format=c2pa sous forme de fichier .c2pa que n’importe quel outil C2PA sait lire. Quiconque peut lire le filigrane peut le récupérer, c’est-à-dire quiconque détient l’image.

curl
curl "https://api.kennmarq.com/v1/credentials/1d455a804e9802a6?format=c2pa" -o credential.c2pa

GET /v1/verify/key

La clé publique du vérificateur : key_id (SHA-256 de son SubjectPublicKeyInfo en DER), algorithm et public_key (PEM). Avec ?key_id= et le kid d’un JWS, la clé qui l’a signé, y compris les clés avec lesquelles nous ne signons plus ; sans ce paramètre, la plus récente.

Une vérification indique ce que porte un fichier, pas qui l’a créé ni s’il a été créé avec l’IA : les marquages peuvent être retirés, et un fichier sans marquage ne prouve rien. Le signataire et la mention d’IA sont ce qu’a déclaré le créateur du fichier.

Erreurs

Erreurs des endpoints du vérificateur
StatutCodeQuand
400invalid_requestAucun fichier, un champ envoyé deux fois, un signed autre que true ou false, une image illisible, ou un ID de filigrane ou un key_id mal formé.
400unknown_fieldUn champ autre que file (ou upload) et signed.
401missing_api_key, invalid_api_key, api_key_revokedUn envoi a été indiqué sans la clé qui l’a créé.
400option_not_availableurl : la vérification d’un fichier par URL n’est pas encore disponible.
404not_foundPas de Content Credentials pour cet ID de filigrane, ou pas de clé de vérificateur avec ce key_id.
404upload_not_foundL’envoi indiqué n’est pas utilisable par cette clé : inconnu, déjà utilisé, expiré ou appartenant à une autre clé.
413payload_too_largeLa requête dépasse 4 Mo : vérifiez les images plus lourdes sur la page du vérificateur, ou envoyez les fichiers via un envoi (jusqu’à 25 Mo).
415unsupported_media_typePas en multipart, ou pas une image, un morceau ou une vidéo que nous savons lire.
429rate_limitedPlus de 10 vérifications par minute ou de 200 par jour depuis une même adresse (une adresse IPv6 compte selon son /64 ; recherches : 120 par minute), les vérifications sans clé du jour sont épuisées pour tout le monde, ou, avec une clé, les appels par minute de votre organisation.
503marker_unavailable, storage_unavailableLe vérificateur (ou les envois) est indisponible, ou un résultat signé n’a pas passé la vérification avant son envoi. Réessayez.