お断り: 本記事は C2PA Technical Specification v2.3(2026年4月時点)と c2pa-rs(コミット 0eeabc7のソースコードを筆者が読解して整理したものです。仕様は継続的に更新されており、細部の記述や用語は変更される可能性があります。実装や設計判断の根拠として用いる際は、必ずC2PA 公式仕様および関連する一次資料をご確認ください。本記事に誤りや古くなった箇所を見つけられた場合は、記事末尾のフィードバック枠よりお知らせいただけると助かります。

はじめに

C2PA(Coalition for Content Provenance and Authenticity)はコンテンツの来歴をメタデータとして埋め込む標準仕様です。SDK を呼び出すだけでも来歴情報の付与・検証は可能ですが、ファイルの中に何がどのような構造で書き込まれているかを把握しておくと、検証ポリシーの設計やトラブルシューティング、独自ワークフローへの組み込みがぐっと楽になります。

本記事は「C2PA 実装入門」シリーズの第3回です。第1回で「なぜ来歴証明が必要なのか」、第2回で「署名から検証までの全体フロー」を押さえた上で、本記事では Manifest の内部に踏み込み、Assertion・Claim・Claim Signature・JUMBF コンテナという4つのレイヤーの役割と関係を概念レベルで整理します。実際のファイルを c2patool で読み出して構造を確認するハンズオンは第4回に続きます。

登場人物と全体像

まず概念レベルで、C2PA Manifest の中に何が入っているかを整理します。

Manifest Store
└── Manifest
    ├── Assertion Store  (Assertion の実体の入れ物)
    │   ├── Assertion
    │   ├── Assertion
    │   └── ...
    ├── Claim            (Assertion への参照リスト+メタ情報)
    └── Claim Signature  (Claim に対する署名)

それぞれの登場人物の役割はこう整理できます。

  • Manifest Store: 1つのアセットに紐付く Manifest 全体の入れ物。ファイル(JPEG・PNG・MP4・PDF など)に埋め込まれる最上位の構造。
  • Manifest: 1回の「このコンテンツに対する来歴宣言」をひとまとめにした単位。時系列で複数の Manifest が連なることもあり、そのうち最後に有効なものが active_manifest として示されます。
  • Assertion: 「いつ・誰が・何をしたか」といった個別の事実。撮影・編集操作・AI 学習可否宣言・作成者の身元情報など、粒度の細かいドキュメントがそれぞれ1つの Assertion になります。実データは Assertion Store に入ります。
  • Claim: Assertion の実データは持たず、代わりに Assertion への参照(hashed URI)をリストアップしたメタ文書です。「どの Assertion を1つの束とみなすか」を決める役割を持ちます。
  • Claim Signature: Claim に対する暗号署名と署名者の証明書チェーンを格納します。Claim 全体の信頼の起点になります。

ここで押さえておきたい重要なポイントがあります。それは「Claim と Assertion は包含関係ではなく並列」ということです。Claim は Assertion の実データを持たず、Assertion Store の中にあるドキュメントを hashed URI で指し示すだけです。あくまで「Assertion Store のこれこれを、まとめて自分の主張とする」という参照リストとして機能します。Claim を1回だけ署名しておけば、参照されている Assertion 全部の改ざん検知が成立する、というのが Claim 層の存在意義です。

仕様書側でこの全体像を1枚で眺められる図として、C2PA Technical Specification v2.3 Chapter 12 “Entity Diagram”(Figure 11 “C2PA Entity Diagram”)があります。Manifest Store・Manifest・Claim・Assertion・Signature の各要素と、それらがアセットや証明書チェーンとどう繋がっているかが1枚の図にまとまっているので、本記事のテキスト解説と行き来しながら眺めると位置関係が掴みやすいです。

C2PA Entity Diagram: Manifest Store・Manifest・Claim・Assertion・Signature とアセット・証明書チェーンの関係を示したクラス図
C2PA Technical Specification v2.3 Chapter 12「Entity Diagram」より Figure 11 "C2PA Entity Diagram"。一次資料はこちら

動画(BMFF)向けには 18.6.5 Fragmented BMFF Entity Diagram(Figure 15)という姉妹図もあります。

なぜこの4つのレイヤーに分けるのか

以降では Manifest の内部を JUMBF コンテナ・Assertion・Claim・Claim Signature の4レイヤーに分けて見ていきますが、この切り分けは単なる整理術ではなく、C2PA が解こうとしている問題のレイヤー分担をそのまま反映したものです。それぞれの層が答えている問いが違います。

レイヤー担当する問い単位
JUMBF コンテナどうやってバイト列として運ぶか、どう addressing するか1つの「箱」(SuperBox)
Assertion何を主張するか1つの粒度の細かい事実
Claimどの事実をひとまとめにして改ざん検知の対象範囲とするか1つの改ざん検知ユニット
Claim Signature誰がその塊を保証するか1つの署名行為

上から順に container / data / integrity / trust の4階層になっており、下の層は上の層の責務を持ち込みません。

  • JUMBF は ISO/IEC 19566-5 で標準化された汎用メタデータコンテナで、C2PA 固有のものではありません。C2PA は「バイト列の物理構造とアドレッシング」という問題を JUMBF に丸投げすることで、自分では運搬形式を発明せずに済んでいます。
  • Assertion は「1つの粒度の細かい事実」で、追加・差し替え・個別検証が Assertion 単位で可能です。生成 AI が書き出したという事実と、EXIF の撮影情報と、元素材のハッシュはそれぞれ別の Assertion として扱われます。
  • Claim は「どの Assertion をひとつの束とみなすか」を決めるメタドキュメントで、束にしたい Assertion のハッシュを自分の中にリストアップします。Claim を1回だけ署名すれば束の中身全部の改ざん検知が成立する、というのが Claim 層の存在意義です。
  • Claim Signature は「その Claim を誰が保証するか」だけに集中した層で、署名値と署名者証明書チェーンだけを持ちます。ここが独立しているおかげで、同じ Claim を別の主体が追認するような運用も原理的に可能になります。

この切り分けが効いてくるのは、たとえば「Assertion だけ追加したい」「Claim はそのままで署名者だけ差し替えたい」といった操作をしたくなったときで、責務が混ざっていると難しいことが、層が分かれているので部品単位で議論・実装できるようになります。

Assertion 個別の事実の積み上げ

Assertion は「いつ・誰が・何をしたか」といった個別の事実を表現する単位です。C2PA 仕様および CAWG(Creator Assertions Working Group)の拡張で標準化されている代表的な Assertion を挙げます。

  • c2pa.actions.v2 編集操作の履歴(created, cropped, color_adjustments, ai_generated など)
  • c2pa.thumbnail.claim Manifest 検証用のサムネイル
  • c2pa.hash.data アセットデータのハッシュ(ハードバインディング)
  • cawg.identity コンテンツ作成者の身元情報(作成者署名付き)
  • cawg.training-mining AI 学習や推論での利用可否の宣言
  • stds.exif EXIF メタデータ
  • stds.iptc IPTC メタデータ

Assertion はそれぞれが独立した CBOR/JSON ドキュメントとして Assertion Store に格納され、Claim から hashed URI で参照されます。これにより、必要な Assertion だけを差し替える・追加する・暗号学的に検証する、といった操作が部品単位で行えます。

特に c2pa.actions は実装上の自由度が高く、編集ツール側の責務でどの操作を記録するかを設計する必要があります。digitalSourceType に IPTC の digitalsourcetype ボキャブラリを入れておくと、生成 AI によるコンテンツか人手による編集かを後段のポリシー判定で機械的に振り分けられるようになります。

CAWG(Creator Assertions Working Group)は C2PA の基本仕様に載らない「作成者主体の身元証明」や「AI 学習可否の宣言」を拡張仕様として整備している団体で、cawg.identity / cawg.training-mining といった Assertion はその成果物です。特に cawg.identity は、Claim Signature とは独立した「作成者本人の署名」を Assertion として持ち込める仕組みになっており、Manifest 全体の信頼モデルに「署名者(パブリッシャー)」と「作成者(クリエイター)」という2系統の主体を同居させることができます。

Claim Assertion を束ねて署名対象を作る

Claim は Manifest の中核となるメタ文書で、CBOR(Concise Binary Object Representation)でシリアライズされます。Claim が担う主な役割は次の3つです。

  1. Assertion Store に格納された個々の Assertion の内容を hashed URI でリストアップする
  2. 署名アルゴリズム、署名者情報、生成時刻などのメタデータを保持する
  3. アセット本体のハードバインディングを含めて、改ざん検知の対象範囲を確定する

Claim 自体が署名対象になるため、Claim 内に Assertion のハッシュを並べておけば、Claim を1回署名するだけで全 Assertion の改ざん検知が可能になります。Merkle ツリーに似た発想ですが、深さ1の単純な構造で目的を果たせる設計です。

Claim v2 では、参照する Assertion のリストが2系統に分かれています。

  • created_assertions: Claim の署名者自身が作成し、内容に責任を負う Assertion。仕様の Trust Model で「All created_assertions are attributed to the signer」と明言される対象です。ハードバインディング(c2pa.hash.data など)や、そのツール自身が行った編集操作(c2pa.actions.v2)が典型的にここに入ります。
  • gathered_assertions: 他の工程や先行 Manifest、ソース素材から引き継いできた Assertion。署名者はパススルーで運んでいるだけで、内容を個別に保証はしません。先行 Manifest から持ち越した cawg.identitystds.exif のようなものがここに入るイメージです。

標準的な Manifest では、created_assertions に少なくとも1つのハードバインディング Assertion が入っていなければならない、という制約があります。2系統の区別は後編で c2patool が出力する created: true フラグとして実際に見えてきます。

Claim v1 にはこの2系統の分離がなく、assertions という1つのフィールドにすべて並びます。v1 から v2 への変更点の1つが、この信頼モデルの粒度を細かくしたことです。

Hard Binding と Soft Binding

C2PA Manifest がアセット本体と紐づいていることを保証する仕組みが Binding です。

Hard Binding は c2pa.hash.data などの Assertion を通じてアセットのバイナリそのもののハッシュを Claim に取り込む方法です。アセットが1ビットでも改変されればハッシュが一致しなくなり、検証が失敗します。最も強い保証が得られる一方、再エンコードや軽微な編集にも反応してしまうため、配信経路でフォーマット変換が発生するシステムでは扱いに注意が必要です。

Soft Binding は知覚的ハッシュ(perceptual hash)やフィンガープリントによって「同一のコンテンツである」ことを緩やかに紐づけます。NSA/CISA の2025年1月ガイダンスでも、Content Credentials の「耐久性(Durability)」を高めるためにデジタル透かしやフィンガープリントとの組み合わせが推奨されています。詳細はNSA・CISAがC2PAを公式推奨 2025年1月ガイダンスを読み解くを参照してください。

Claim Signature 信頼の起点

Claim Signature は COSE_Sign1(RFC 9052)形式で表現される署名構造で、Claim の CBOR バイト列に対する署名と署名者の X.509 証明書チェーンを格納します。alg には C2PA が許容する署名アルゴリズムの識別子(Ps256, Es256, Ed25519 など)が入り、生成時には RFC 3161 タイムスタンプ局から得られた署名時刻も付与できます。

C2PA は Web PKI とは別系統の信頼モデルを採用しており、C2PA Trust List に登録された認証局から発行された証明書が信頼の起点となります。検証側は証明書チェーンを Trust List に対して検証し、Claim 署名がそのチェーンによって正当に行われたことを確認することで、Manifest 全体を信頼できる状態に置きます。

これにより「誰が署名したか」を追跡可能にしつつ、必要に応じて失効リスト(CRL/OCSP)を通じて鍵漏えい時の影響範囲を制御できる枠組みになっています。また、CAWG の cawg.identity のように「作成者が自分の身元を Assertion の中で別途署名する」機構と組み合わせることで、Claim 署名者(パブリッシャー)と作成者(クリエイター)が別々の鍵で信頼を形成する運用も可能です。

JUMBF Manifest を運ぶ箱

ここまで見てきた Assertion / Claim / Claim Signature をバイト列として実際のファイルに埋め込むのが JUMBF(JPEG Universal Metadata Box Format)です。C2PA は独自のシリアライズ形式を新たに定義する代わりに、ISO/IEC 19566-5 で標準化された JUMBF を採用しています。JUMBF は元々静止画向けに設計されたメタデータコンテナですが、C2PA ではアセット種別を問わない汎用コンテナとして使用しています。

JUMBF の SuperBox は Content Type を示す 16 バイトの UUID と、Description Box が保持するラベル文字列を持ちます。C2PA では両方を使い分けていて、型の識別には UUID を、URI でのアドレッシングには人間に読めるラベルを用いる構造です。

c2pa-rs の実装を見ると、C2PA が使う Content Type UUID とラベルの対応は次のようになっています(UUID は 6332XXXX00110010800000AA00389B71 という ISO 19566 系の共通パターンで、先頭4バイトが ASCII 名に対応します)。

Content Type UUID(先頭4バイト)ラベル役割
c2pac2paManifest Store ルート
c2mamanifest URN(例: urn:c2pa:...通常の Manifest
c2ummanifest URNUpdate Manifest
c2asc2pa.assertionsAssertion Store
c2clc2pa.claim または c2pa.claim.v2Claim
c2csc2pa.signatureClaim Signature
json / cbor各 Assertion 固有のラベル個別の Assertion

Assertion を指す hashed URI は self#jumbf=c2pa/<manifest-urn>/c2pa.assertions/<assertion-label> の形式で、ラベルを / で連結したパスになっています。この設計のおかげで、JUMBF をパースできるツールチェーンがあれば C2PA Manifest を構造的に取り出すことができます。

Manifest Store はファイル形式に応じた埋め込み方法でアセット内に格納されます。JPEG では APP11 マーカー、PNG では専用チャンク、MP4 では ISO BMFF の box、PDF では ExtensionSchema を用いる、というように、既存のコンテナ仕様の拡張点に相乗りする設計です。実装側の負担を抑えつつ、既存のメディア処理パイプラインに組み込みやすいのが利点です。

まとめと続編

C2PA Manifest は JUMBF というコンテナの上に Assertion・Claim・Claim Signature という3層を積み上げた構造をしています。仕様自体は重厚に見えますが、要素を分解してしまえば「個別の事実(Assertion)をハッシュ参照で束ねる(Claim)、その束に署名する(Claim Signature)、全体を運ぶ箱(JUMBF)」という素直なレイヤリングで成り立っており、エンジニアにとって理解しやすい設計です。

ここまで概念レベルで整理してきた構造が、実際のファイルの中にどう現れるかを段階的に確認したい方は、第4回 c2patool で Manifest を覗いてみるに進んでください。c2pa-rs 付属のテスト画像を c2patooljq で読み出しながら、本記事で扱った登場人物が JSON の中にそのまま現れる様子を追いかけ、最後に検証パイプライン設計の観点までつなげます。

参考リンク


TechThanks は Content Credentials の実装支援に取り組んでいます。C2PA SDK の導入や検証パイプラインの設計についてお気軽にご相談ください。