お断り: 本記事は C2PA Technical Specification v2.3(2026年4月時点)と c2pa-rsc2patool のソースコードを筆者が読解して整理したものです。Soft Binding は仕様で算法(algorithm)レジストリが拡張され続けている領域であり、SDK の対応状況も変化します。実装や設計判断の根拠として用いる際は、必ずC2PA 公式仕様および各 SDK の最新ドキュメントをご確認ください。本記事に誤りや古くなった箇所を見つけられた場合は、記事末尾のフィードバック枠よりお知らせいただけると助かります。

はじめに

C2PA における「バインディング(binding)」とは、Manifest と当該コンテンツを紐付ける仕組みのことです。仕様書では大きく Hard BindingsSoft Bindings の二系統に分けて議論されており、Manifest に必須な hard binding assertion がコンテンツの「正確な同一性」を保証する一方、soft binding assertion はリフローや再エンコードを経た後でも対応する Manifest を引けるようにする補完的な役割を担います。

本記事は、シリーズ「C2PA 実装入門」 の応用編として、ハードバインディングとソフトバインディングを実際に実装し、両者の挙動の違いを手元で確認することを目的とします。

Hard Binding と Soft Binding は何が違うか

両者の役割を一覧で整理します。

観点Hard BindingSoft Binding
目的バイナリ単位で Manifest と完全一致するコンテンツであることを保証コンテンツが変換・再エンコードされても Manifest を再発見できるようにする
代表的な assertion ラベルc2pa.hash.data / c2pa.hash.boxes / c2pa.hash.bmff.v2c2pa.soft_binding
アルゴリズムSHA-256 / SHA-384 / SHA-512 などの暗号学的ハッシュ知覚ハッシュ、コンテンツフィンガープリント、電子透かしなど
失敗時の意味コンテンツが Manifest と一致しない(改ざんまたは別ファイル)直接の対応は取れない(あるいは候補が見つからない)
必須性Manifest に1つ必要(Section 11 参照)任意

ハードバインディングは「いま手元にあるバイト列がこの Manifest が署名した対象と完全に同じか」を判定します。1ビットでも異なればハッシュは一致しません。一方ソフトバインディングは、画像をリサイズしたり、動画を別コーデックに再エンコードしたりしても、コンテンツ識別子(指紋や透かし)から元の Manifest を辿れるようにする仕組みです。

仕様書では soft binding を「the relationship between an asset and its manifest that allows the recovery of the manifest from a transformed version of the asset」と説明しています(Section 18)。

どちらをいつ使うか

実運用では片方ではなく、目的に応じて組み合わせて使います。

  • 配信前後でコンテンツが変わらないユースケース(社内アーカイブ、原版管理、原稿の正本性確認など): hard binding だけで十分
  • SNS や CDN 経由で再エンコードされる前提のユースケース(メディア配信、報道写真、生成 AI 画像の出所表示など): hard binding に加えて soft binding を併用し、変換後でも「Manifest Recovery」 で来歴を辿れるようにする

C2PA は Manifest をファイル内に埋め込むのが基本ですが、SNS にアップロードされる過程でメタデータが除去されることは珍しくありません。soft binding と Manifest Repository(外部に Manifest を保存しておく仕組み)を組み合わせると、メタデータが剥がれた配信物からも来歴を復元できる可能性が出てきます。

ハードバインディングの実装

c2pa-rs と c2patool では、署名時に hard binding assertion が自動的に付与されます。明示的に書かなくても、SDK が対象ファイルの形式に応じて適切なアルゴリズム(多くの画像で c2pa.hash.data、BMFF 系コンテナで c2pa.hash.bmff.v2)を選んでハッシュを計算してくれます。

c2patool での確認

第5回で作成したテスト用証明書 (certs/test.pem / certs/test.key) と入力画像 input.jpg がある前提で、最小構成の Manifest 定義を用意します。

{
  "claim_generator": "techthanks-binding-demo/0.1.0",
  "title": "binding demo",
  "assertions": [
    {
      "label": "c2pa.actions",
      "data": {
        "actions": [
          { "action": "c2pa.created" }
        ]
      }
    }
  ]
}

これを manifest-hard.json として保存し、c2patool で署名します。

c2patool input.jpg \
  --manifest manifest-hard.json \
  --signer-config '{"alg":"ps256","sign_cert":"certs/test.pem","private_key":"certs/test.key"}' \
  --output signed-hard.jpg

署名後の Manifest を読み出すと、明示的に記述していないにもかかわらず c2pa.hash.data が assertion として登場します。

c2patool signed-hard.jpg | jq '.manifests[].assertions[] | select(.label | startswith("c2pa.hash."))'

出力例(抜粋):

{
  "label": "c2pa.hash.data",
  "data": {
    "exclusions": [
      { "start": 12345, "length": 6789 }
    ],
    "name": "jumbf manifest",
    "alg": "sha256",
    "hash": "<base64>",
    "pad": "<base64>"
  }
}

exclusions は「ハッシュ計算から除外するバイト範囲」で、Manifest 自身が埋め込まれる JUMBF 領域がそこに該当します。Manifest を含めてハッシュを取ると無限ループになるため、署名対象から自分自身の領域を外す設計になっています。

検証時にはこの exclusions を除いた残りバイト列のハッシュを再計算し、hash と一致するかを確認します。1バイトでも異なれば検証失敗です。

c2pa-rs での挙動

Rust 側でも同じです。Builder でアセットを書き出すと、内部で add_data_hash 相当の処理が走り、hard binding assertion が組み立てられます。利用者が明示的に追加する API は基本的に不要で、独自にカスタムしたい場合に限り Builder::add_assertionc2pa.hash.data を上書きする、といった使い分けになります。

実装のおおよその流れは次の通りです。

  1. Builder にユーザー由来の assertion(c2pa.actions など)を積む
  2. Builder::sign を呼ぶと、対象ファイルへ Manifest を仮配置してその領域を exclusions に登録
  3. exclusions を除いたバイト列のハッシュを計算し、c2pa.hash.data assertion として Claim に組み込む
  4. Claim 全体に署名し、Claim Signature と一緒に Manifest として埋め込む

この設計のおかげで、開発者は Hard Binding を「常にあるもの」として扱えます。ただし大容量ファイルを扱う場合のハッシュ計算コストや、ストリーミング配信時の挙動については、第7回本番運用に向けてで触れたパフォーマンス観点と合わせて検討しておくと安心です。

ソフトバインディングの実装

ソフトバインディングは、対応するアルゴリズム(alg)と、コンテンツから抽出した識別子(指紋や透かし)を c2pa.soft_binding assertion として Manifest に追加します。assertion の構造は仕様書 Section 18.2 で定義されており、概ね以下のフィールドで構成されます。

フィールド役割
algソフトバインディングアルゴリズムの識別子(ICANN ドメイン形式の名前を仕様が推奨)
blocksアルゴリズムが算出した識別子のリスト。各エントリは scopevalue を持つ
alg_paramsアルゴリズム固有のパラメータ(任意)
urlアルゴリズムの公開仕様への URL(任意)
description補足説明(任意)

blocksscope は「このブロックがコンテンツのどの範囲に対応しているか」を示し、画像なら全体、動画なら時間範囲やフレーム範囲を表す形が一般的です。

c2patool で soft binding を追加する

ハードバインディングと同様に、ユーザー定義の assertion として manifest 定義に書き加えます。実コンテンツから識別子を計算する処理は外部ツールやサービスに任せ、その結果値を埋め込む流れになります。例として、自作のフィンガープリントアルゴリズム example.com/perceptual-hash を想定した Manifest 定義は次のようになります。

{
  "claim_generator": "techthanks-binding-demo/0.1.0",
  "title": "binding demo with soft binding",
  "assertions": [
    {
      "label": "c2pa.actions",
      "data": {
        "actions": [
          { "action": "c2pa.created" }
        ]
      }
    },
    {
      "label": "c2pa.soft_binding",
      "data": {
        "alg": "com.example.perceptual-hash.v1",
        "url": "https://example.com/specs/perceptual-hash-v1.html",
        "description": "Perceptual hash extracted from the full frame.",
        "blocks": [
          {
            "scope": { "start": 0, "length": 0 },
            "value": "<base64-encoded-fingerprint>"
          }
        ]
      }
    }
  ]
}

ここで <base64-encoded-fingerprint> には、別途算出した知覚ハッシュや透かし埋め込みパラメータを Base64 化した値を入れます。署名コマンドはハードバインディングのときと同じです。

c2patool input.jpg \
  --manifest manifest-soft.json \
  --signer-config '{"alg":"ps256","sign_cert":"certs/test.pem","private_key":"certs/test.key"}' \
  --output signed-soft.jpg

c2patool signed-soft.jpg | jq '.manifests[].assertions' で読み出すと、c2pa.hash.data(自動付与)と c2pa.soft_binding(明示追加)の双方が並んでいるはずです。

c2pa-rs で soft binding を追加する

Rust から同じ assertion を付ける場合は Builder::add_assertion を使います。

use c2pa::Builder;
use serde_json::json;

let mut builder = Builder::from_json(r#"{
    "claim_generator": "techthanks-binding-demo/0.1.0",
    "title": "binding demo with soft binding"
}"#)?;

let soft_binding = json!({
    "alg": "com.example.perceptual-hash.v1",
    "url": "https://example.com/specs/perceptual-hash-v1.html",
    "description": "Perceptual hash extracted from the full frame.",
    "blocks": [
        {
            "scope": { "start": 0, "length": 0 },
            "value": "<base64-encoded-fingerprint>"
        }
    ]
});

builder.add_assertion("c2pa.soft_binding", &soft_binding)?;

Builder::sign を呼んだ時点で、ハードバインディングは内部生成、ソフトバインディングは追加した assertion がそれぞれ Claim の参照対象に並びます。

なお c2pa-rs 側ではソフトバインディングの「アルゴリズム本体」は提供していません。指紋や透かしを実際に抽出・埋め込みする実装は別ライブラリで行い、c2pa-rs は得られた値を assertion として Manifest に格納する役回りに徹します。アルゴリズムの選定は、後述するレジストリと運用要件に合わせて決定します。

検証側はどう振る舞うか

Manifest を受け取った検証側は、まずハードバインディングを評価します。c2pa.hash.data などの assertion が示すバイト範囲のハッシュを再計算し、一致すれば「このバイト列は Manifest が署名したものと完全に同一」と判定します。一致しなければ検証は失敗で、ここで処理を打ち切るのが基本です。

ソフトバインディングはハードバインディングが取れなかった場合や、配信物から Manifest 自体が剥がれていたときの「リカバリ経路」として活用されます。仕様書の Manifest Recovery では、検証側が次のような流れで Manifest を再発見するシナリオが想定されています。

  1. 受け取ったコンテンツに Manifest が埋め込まれていない、あるいは破損している
  2. ソフトバインディングアルゴリズムを使って、コンテンツから指紋や透かし値を抽出
  3. その値で外部の Manifest Repository を検索し、対応する Manifest 候補を取得
  4. 候補の Manifest と現在のコンテンツとの整合を確認し、来歴を提示

この経路を実用にするには、検証側もソフトバインディングのアルゴリズムを実装している必要があります。alg フィールドが ICANN ドメイン形式で命名されるのは、誰がどのアルゴリズムを定義しているかを名前空間で明示するためで、検証実装と Manifest 側の alg 表記が一致しないとリカバリは成立しません。

アルゴリズム選定の現実的な観点

ソフトバインディングを実用するうえで悩ましいのが、アルゴリズム選定です。仕様書はアルゴリズム名の付け方とレジストリのあり方を定めますが、具体的な算法は別の標準化やオープンソース実装に委ねられています。

代表的な選択肢の方向性を整理します。

  • 知覚ハッシュ系: pHash や DCT 系の指紋。OSS 実装が多く、画像のリサイズや軽い色補正には強い反面、トリミングや回転には弱い
  • ディープラーニング系の埋め込みベクトル: より頑健な一致判定が可能だが、学習済みモデルの互換性管理が継続的な負担になる
  • 電子透かし: コンテンツに目に見えない透かしを埋め込む方式。再エンコードに強い反面、生成側で透かしを入れる工程が必要

検証側の実装負担、頑健性、誤一致率のバランスを見て選定し、選んだアルゴリズム名(alg)の意味を仕様化したうえで url から辿れる状態にしておくのが、相互運用を成立させるうえで重要です。

まとめ

C2PA の Hard Binding と Soft Binding は、対立概念ではなく補完関係にあります。Hard Binding は「Manifest と完全一致するコンテンツである」ことの証明、Soft Binding は「変換後でも Manifest を辿れる」ことの保険です。c2pa-rs と c2patool は前者をほぼ自動で扱ってくれますが、後者は運用要件に合わせて自分でアルゴリズムを選び、assertion として組み込む設計判断が必要になります。

TechThanks では、生成 AI 由来コンテンツやメディア配信における C2PA 導入の支援を行っています。Hard / Soft Binding の組み合わせ設計、Manifest Repository の構築、検証パイプラインの実装まで含めた相談がありましたら、お気軽にお問い合わせください。