- PVSM.RU - https://www.pvsm.ru -
13 августа Google выложила Credentio — C++ библиотеку для проверки C2PA Content Credentials. Я решил собрать её и сделать простой тест: кидаешь картинку и смотришь, можно ли понять, камера это, генератор или редактор.
Спойлер: нельзя. Причём ограничение оказалось не в Credentio — я просто неправильно понимал, какую задачу вообще решает C2PA. Он не анализирует пиксели и не пытается угадать происхождение изображения. Если в файле нет C2PA‑данных, проверять ему попросту нечего.
В итоге вместо «детектора AI‑картинок» у меня получился небольшой локальный валидатор, а сам эксперимент оказался полезнее именно из‑за того, что первоначальная гипотеза не сработала.
Мой исходный сценарий был таким: редакция, агентство или обычный пользователь получает изображение и хочет понять, откуда оно взялось.
В идеальном мире проверка возвращала бы что‑то вроде:
|
Результат |
Что хотелось бы узнать |
|
Camera |
Файл получен с камеры |
|
AI |
Изображение создано генеративной моделью |
|
Edited |
Файл редактировали |
|
Unknown |
Данных недостаточно |
На практике C2PA решает другую задачу.
Он не запускает классификатор изображения, не ищет характерные следы генерации и не сравнивает картинку с базой оригиналов. Если в файле нет данных C2PA, стандарту просто нечего проверять.
Это был первый момент, когда моя первоначальная идея начала разваливаться.
Content Credentials можно грубо представить как подписанную историю происхождения цифрового файла.
Технически речь идёт о C2PA Manifest. В нём находятся claims и assertions с информацией, которую решил записать создатель или приложение, а также данные, позволяющие связать этот manifest с конкретным asset и проверить цифровую подпись.
В assertions могут быть сведения о приложении, устройстве, действиях редактирования, использовании генеративной модели и другие данные. Но наличие конкретного поля зависит от того, кто создал Content Credentials.
То есть C2PA отвечает не на вопрос:
Это настоящая фотография или AI?
А скорее на такой:
Есть ли у этого файла проверяемая история происхождения, кто её подписал и соответствует ли текущее содержимое тому, с чем была связана подпись?
Для связи с содержимым могут использоваться hard bindings, в частности криптографические хеши. Если содержимое меняется так, что binding больше не совпадает, валидатор это обнаруживает.
Есть ещё одна граница, которую поначалу легко пропустить. Валидный C2PA Manifest не доказывает истинность того, что изображено на фотографии или видео.
Если подписанный файл содержит постановочную сцену, сама криптография не превращает её в документальное событие. Она позволяет проверить происхождение заявленных данных и их целостность.
Интерфейс нельзя строить вокруг двух состояний «валидно» и «подделка». Практически полезных состояний как минимум три.
Manifest найден, необходимые проверки проходят, цифровая подпись валидируется, а signing credential может получить статус signingCredential.trusted.
Здесь есть техническая тонкость. Я сначала формулировал это как «сертификат входит в доверенный список», но это неточно.
В C2PA trust list содержит trust anchors. Валидатор строит и проверяет цепочку сертификатов до подходящего trust anchor из настроенного trust store. Если цепочка проходит проверку, signing credential получает trusted status.
И даже после этого остаётся отдельный вопрос: доверяю ли я самому подписанту и его заявлениям.
Manifest есть, но одна или несколько проверок завершаются failure.
В моём тесте с изменённым JPEG Credentio возвращает:
assertion.dataHash.mismatch
Это уже конкретный технический результат: текущее содержимое не соответствует хешу, с которым связан manifest.
Он заметно полезнее абстрактного красного Invalid, потому что показывает, какая именно проверка не прошла.
Если Credentio не находит Manifest Store, из этого нельзя сделать вывод, что файл поддельный, настоящий или сгенерирован нейросетью.
Отсутствие manifest вообще мало что говорит о происхождении файла: это может оказаться и обычное фото, и генерация, и банальный скриншот. Плюс метаданные могли потеряться после редактора или перекодирования.
Поэтому в интерфейсе я специально вынес No Content Credentials в отдельное нейтральное состояние. Красная ошибка здесь была бы просто неправильной интерпретацией результата.
Credentio Google представила 13 августа 2026 года. Библиотека написана на C++ и на момент анонса сфокусирована именно на validation Content Credentials.
Меня зацепили две вещи.
Первая: проверку можно выполнять локально. Для валидатора происхождения это выглядит логично. Не хочется отправлять исследуемую фотографию, документ или видео на внешний сервер только ради разбора уже встроенных в файл данных.
Вторая: Credentio возвращает подробный результат проверки, а не только один boolean. В CLI можно получить crJSON и увидеть manifests, validation results, signature information и конкретные status codes.
Для экспериментов этого достаточно, но читать сырой вывод каждый раз неудобно. Поэтому я решил завернуть validator в маленький локальный web UI.
Схема получилась без отдельного backend‑сервиса в интернете:
Browser
↓
Local Node.js server on 127.0.0.1
↓
Temporary file
↓
Credentio C++ validator
↓
crJSON
↓
Human-readable report in browser
Пользователь выбирает файл в браузере. Браузер отправляет его Node.js серверу, который слушает только 127.0.0.1.
Сервер создаёт временный каталог, записывает туда asset с правами 0600, запускает собранный бинарник Credentio и получает crJSON. После завершения проверки временный каталог удаляется в finally.
Из результата я формирую краткую сводку: количество манифестов, успешных и неуспешных проверок, число доверенных подписей, имя подписанта и список конкретных ошибок. Полный crJSON при этом можно открыть отдельно.
Здесь у меня не было цели построить сложную security‑модель. Хотелось просто не создавать лишний сетевой контур там, где он вообще не нужен.
Выбранный asset не отправляется в Google или другой cloud API. Сервер жёстко привязан к 127.0.0.1, cross‑origin POST requests отклоняются, а UI отдаётся с ограничивающей Content Security Policy и дополнительными security headers.
Есть и лимит размера файла. По умолчанию это 2 GiB, его можно изменить через MAX_UPLOAD_BYTES.
При этом назвать проект полностью offline было бы неправильно. Интернет нужен на этапе первоначальной настройки: setup загружает исходники зависимостей, trust lists и публичные test assets. После этого сама validation выбранного файла выполняется локально.
В проект я добавил четыре публичных файла с разными ожидаемыми результатами:
|
Файл |
Ожидаемый результат |
|---|---|
|
|
Валидные Content Credentials |
|
|
Content hash mismatch |
|
|
Content Credentials отсутствуют |
|
|
Валидные Content Credentials у видео |
Это не просто кнопки в интерфейсе. Те же четыре случая проходят через integration test с собранным Credentio.
Для good.jpg в моём запуске получилось:
4 manifests
40 successful checks
0 failures
4/4 manifests have a trusted signing credential
Signer: Google LLC
На этом примере хорошо видно, во‑первых, validation не нашла нарушения целостности, во‑вторых, signing credentials получили trusted status в используемой конфигурации trust lists.
bad.jpg связан с C2PA данными, но одна проверка не проходит.
Результат:
4 manifests
39 successful checks
1 failure
assertion.dataHash.mismatch
На этом примере хорошо видно, почему самого факта наличия подписи недостаточно. Validator отдельно проверяет, соответствует ли текущее содержимое данным, которые должны его связывать с Manifest.
Для plain.jpg Credentio не находит Content Credentials.
Именно здесь я окончательно отказался от идеи показывать пользователю универсальный verdict вида «real / fake». У валидатора нет данных, из которых такой вывод можно было бы получить.
Тот же механизм работает не только с JPEG. Для тестового good.mp [1]4 у меня получилось:
2 manifests
19 successful checks
0 failures
2/2 trusted manifests
В самом локальном интерфейсе я разрешил расширения, которые поддерживает закреплённая сборка Credentio: изображения, видео, аудио и документы, включая PDF, DOCX, PPTX и XLSX.
Credentio собирается через Bazel, и для небольшого демо можно было бы просто брать текущий main.
Но тогда результат эксперимента зависел бы от состояния нескольких внешних репозиториев в день запуска. Мне хотелось, чтобы через неделю та же команда собирала то же окружение.
Поэтому setup фиксирует конкретные revisions для Credentio, LibCppBor, C2PA trust lists и test assets. Для загружаемых trust lists и sample files проверяются SHA-256 checksums. Исходники, бинарник, trust lists и тестовые медиа складываются в .runtime/, который не коммитится в Git.
На macOS запуск выглядит так:
npm install
npm run setup
npm start
После этого UI доступен по адресу:
http://127.0.0.1:3210
Сейчас setup рассчитан на macOS, требует Node.js 20+, Git и Xcode Command Line Tools. Я тестировал проект на Apple Silicon.
Первая native build занимает несколько минут. Дальше Bazel использует локальный cache, поэтому повторные запуски уже не требуют пересобирать всё с нуля.
Здесь проще перечислить ограничения напрямую:
он не определяет AI‑контент по пикселям, кадрам или аудио;
не ищет исходный файл в интернете;
не доказывает, что изображённое событие действительно происходило;
не восстанавливает потерянный C2PA Manifest;
не добавляет и не подписывает новые Content Credentials.
Последний пункт относится и к текущему Credentio: в анонсе Google пишет, что библиотека сейчас сфокусирована на validation, а генерацию и embedding Content Credentials планируют добавить позже.
Я начал с вопроса: можно ли взять произвольную картинку и надёжно определить, где она создана и использовалась ли нейросеть.
С помощью одного C2PA ответить на этот вопрос нельзя.
Если Content Credentials нет, стандарт не даёт основания классифицировать файл. Если они есть, ситуация становится гораздо интереснее: можно проверить Manifest, криптографическую связь с asset, цифровую подпись, цепочку доверия и конкретные provenance assertions.
То есть C2PA полезен не потому, что умеет сам распознавать истину. Он даёт инфраструктуру, в которой участники цепочки могут оставлять проверяемые заявления о происхождении контента.
Для этого, конечно, сама цепочка должна работать. Камера или генератор должны записать provenance data, редактор должен их сохранить или дополнить, платформа не должна всё выбросить при перекодировании, а получатель должен уметь проверить подпись и решить, доверяет ли подписанту.
После эксперимента я бы уже не стал называть такой инструмент «AI detector». Это локальный provenance validator.
Google Developers Blog, анонс Credentio: https://developers.googleblog.com/introducing‑credentio‑open‑source‑c‑library‑for‑c2pa‑content‑credentials‑from‑google/ [2]
C2PA Technical Specification 2.4: https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html [3]
Credentio source: https://mediaprovenance.googlesource.com/credentio/ [4]
Исходный код моего прототипа: https://github.com/vbel254/credentio-local-checker [5]
Страница проекта: https://voolee.tech/ru/projects/credentio-local-checker/ [6]
Автор: vbel
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/open-source/456819
Ссылки в тексте:
[1] good.mp: http://good.mp
[2] https://developers.googleblog.com/introducing‑credentio‑open‑source‑c‑library‑for‑c2pa‑content‑credentials‑from‑google/: https://developers.googleblog.com/introducing-credentio-open-source-c-library-for-c2pa-content-credentials-from-google/
[3] https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html: https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html
[4] https://mediaprovenance.googlesource.com/credentio/: https://mediaprovenance.googlesource.com/credentio/
[5] https://github.com/vbel254/credentio-local-checker: https://github.com/vbel254/credentio-local-checker
[6] https://voolee.tech/ru/projects/credentio-local-checker/: https://voolee.tech/ru/projects/credentio-local-checker/
[7] Источник: https://habr.com/ru/articles/1071706/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1071706
Нажмите здесь для печати.