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

はじめに

本記事は、シリーズ「C2PA 実装入門」 の応用編・ソフトバインディング編です。前編にあたるハードバインディング実装 では、c2pa-rs が c2pa.hash.data を自動付与してバイト単位の完全一致を保証する仕組みと、1 バイトの改変で assertion.dataHash.mismatch が飛ぶところまでを押さえました。

ハードバインディングは強力な反面、JPEG 再エンコードや SNS 投稿時のメタデータ除去であっさり壊れます。そこを埋めにいくのがソフトバインディングです。本記事では、C2PA 公認のソフトバインディングアルゴリズムレジストリに登録された Adobe Trustmark を使って、画像に不可視透かしを焼き付けつつ c2pa.soft-binding assertion として Manifest にも同じ識別子を残す流れを、Python のハンズオンで一気通貫に組んでいきます。

本記事で組み立てるパイプラインは、C2PA 仕様の別文書 C2PA Soft Binding API (Decoupled) で規定されている Manifest Recovery のフローを、com.adobe.trustmark.Q という具体的なアルゴリズムで実装したものになります。とくに Section 1.2.1.1 “Watermarking algorithms” で描かれている「Manifest が欠落した asset から不可視透かしを抽出し、soft binding algorithm list と resolution API を辿って active manifest を復元する」フローを参考設計とし、本記事では手を動かしていきます。

透かし埋め込みから検証までの流れ

本記事のハンズオンは次の順序で進めます。最初に Trustmark で画像に payload(短い識別子)を焼き付け、その透かし入り画像に対して c2pa-python で c2pa.soft-binding assertion を持つ Manifest を付与します。検証側は、Manifest に記録された value と画像から取り出した透かしの payload を突き合わせて、Manifest Recovery が成立するかを確かめる、という流れです。

flowchart TD A(["1. 元画像"]) B(["2. 透かし埋め込み(Trustmark)"]) C(["3. c2pa-python で署名"]) D(["4. 配信"]) E(["5. 検証 / Manifest Recovery"]) A --> B --> C -.-> D -.-> E

「透かしを埋め込んでから C2PA の署名をする」という順番が大事です。Trustmark は画像のピクセル値を書き換えるので、署名後に透かしを入れるとハードバインディング (c2pa.hash.data) があっさり壊れてしまいます。先に透かしを焼き付けてから、その状態の画像に対して Manifest を付与する。この順序で組むことで、ハードとソフトの両方が成立した出力が手に入ります。

この順序は、後半の Manifest Repository の設計にも効いてきます。透かしから payload を取り出してレジストリを引いた先に置いておくべき Manifest は、透かしを入れる前のものではなく、透かしを入れた後のアセットを説明する Manifest です。透かし前のハッシュを持つ Manifest を登録してしまうと、復元しても配布されている画像と c2pa.hash.data が合わず、復元する意味が失われます。Guidance for Implementers Section 4.2.4 も、透かしを入れて Manifest に記録したあとのものを指して「その Manifest は、作成から透かしの適用までを含む来歴を説明する」と書いています。

That C2PA Manifest describes the provenance of the video from creation up to and including the watermark being applied.

すでに署名済みのアセットに後から透かしを入れたい場合は、透かしを入れた時点で既存 Manifest のハードバインディングが無効になるので、元の Manifest を ingredient(parentOf)に持つ新しい Manifest で署名し直すことになります。Soft Binding API の UC1 が “signing the asset with a further manifest” と書いているのがこのパターンです。いずれの経路でも、レジストリに載るのは透かし入りアセットの active manifest です。

ソフトバインディング assertion の構造

ソフトバインディングは、対応するアルゴリズム(alg)と、コンテンツから抽出した識別子(指紋や透かし)を c2pa.soft-binding assertion として Manifest に積みます。assertion のラベルはハイフン区切りの c2pa.soft-binding で、構造は仕様書 Section 18.10 “Soft Binding” の CDDL で定義されています。

フィールド役割
algソフトバインディングアルゴリズムの識別子(ICANN ドメイン形式の名前を仕様が推奨)
blocksアルゴリズムが算出した識別子のリスト。各エントリは scopevalue を持つ(どちらも必須)
nameこの binding が何を覆っているかを示す人間可読の説明(任意)
alg-paramsアルゴリズム固有のパラメータ(任意)。アンダースコアではなくハイフン
bindingMetadata実装者や連絡先などの補足メタデータ(任意)。description フィールドはこの中に入る
url非推奨。asset reference assertion に置き換えられたので新規に書いてはいけない

blocks[].scope に入れられるのは timespan(音声・動画の時間範囲)と region(画像の領域)で、以前あった extent は非推奨です。画像全体を覆う透かしのように絞り込む対象がない場合は、空オブジェクト {} を渡します。省略はできず、scope ごと落とすと Error: claim could not be converted from CBOR になって Manifest が読めなくなります。

仕様書(Section 9.3.2 “Referenced List of Soft Binding Algorithms”)では、alg の選び方について次のように書かれています。

Soft bindings are generated by an algorithm named in the alg field of the soft binding assertion. The algorithm name should be among those algorithms listed in the soft binding algorithm list as supported by this specification.

登録済みアルゴリズムの一覧は c2pa-org/softbinding-algorithm-list に JSON で管理されています。本記事ではその中から、Adobe が公開している透かし方式 Adobe Trustmarkalg = com.adobe.trustmark.Q)を使います。OSS 実装が pip で入る、画像への不可視透かし埋め込みから抽出まで Python で完結する、というあたりがハンズオンにちょうどいいです。

Trustmark で透かしを埋め込む

第5回で使った uv で Python プロジェクトを立ち上げます。Trustmark は PyTorch に依存するので、インストールには少し時間がかかります。

cd /tmp
mkdir -p c2pa-soft-binding && cd c2pa-soft-binding
uv init .
# Trustmark は Python 3.10〜3.12 に対応。uv の requires-python を合わせる
# uv init が書く既定値はバージョンによって変わるので、行ごと置き換える
sed -i '' -E 's/^requires-python = .*/requires-python = ">=3.10,<3.13"/' pyproject.toml
uv python pin 3.11
uv add trustmark c2pa-python

# サンプル画像と c2pa-rs のテスト用証明書をダウンロード
curl -sL -o input.jpg https://raw.githubusercontent.com/contentauth/c2pa-rs/main/sdk/tests/fixtures/earth_apollo17.jpg
curl -sL -o test_cert.pem https://raw.githubusercontent.com/contentauth/c2pa-rs/main/sdk/tests/fixtures/certs/es256.pub
curl -sL -o test_key.pem https://raw.githubusercontent.com/contentauth/c2pa-rs/main/sdk/tests/fixtures/certs/es256.pem

# 以降のステップで作成する Python スクリプトを先に用意しておく
touch embed_watermark.py sign_soft_binding.py recover_manifest.py

この時点でプロジェクトはこんな構成になっています(.venv__pycache__ は省略)。

c2pa-soft-binding/
├── embed_watermark.py
├── input.jpg
├── main.py
├── pyproject.toml
├── README.md
├── recover_manifest.py
├── sign_soft_binding.py
├── test_cert.pem
├── test_key.pem
└── uv.lock

main.pyREADME.mduv init が自動生成したもので、本記事では使いません。作成済みの embed_watermark.py sign_soft_binding.py recover_manifest.py に順番に中身を書いていきます。

透かしと payload の関係

透かしを埋め込むというのは、ここでは「画像の全ピクセルに人間の目では知覚できない程度の微小な変調を加えて、短いビット列(payload)を画像データそのものに織り込む」操作を指します。Trustmark は学習済みのニューラルネットワークがエンコーダ・デコーダの役割を担っていて、元画像と payload を渡すと見た目をほとんど変えない範囲でピクセル値を調整してくれて、逆に画像を渡すとそこから payload に相当するビット列を推定して返してくれる、という構成です。元画像とほぼ見分けのつかない絵のまま、ファイル内部に識別子が紛れ込んでいる、と捉えてもらうのが近そうです。仕組みの詳細は Adobe Trustmark の READMEICCV 2025 採録の論文 PDF を参照してください。

payload は画像に結びつけたい任意の短い識別子です。内容そのものに来歴情報を詰め込むのではなく、「この画像は TT0001 として登録されているもの」というラベルだけを物理的に焼き付ける役割で、C2PA soft binding の文脈では後述のとおり Manifest を引くためのキーとして使います。Trustmark Q バリアントには英数字 7 文字くらいしか入らないので、本記事では TT0001 という最小限のダミー ID を使っています(TT は TechThanks の TT です)。C2PA のソフトバインディングレジストリには softBindingResolutionApis フィールドを持つアルゴリズムが登録されていて、画像から取り出した payload を解決 API に渡し、それに対応する Manifest を引き当てる設計が想定されています(例: com.aiwatermark.pixelseal.1、登録エントリは c2pa-org/softbinding-algorithm-list を参照)。

透かしはハードバインディングのバイト単位ハッシュとは違って、JPEG 再エンコードやリサイズみたいな「見た目がほぼ同じままの変換」であれば生き残るよう設計されています。おかげで、Manifest ごとメタデータが剥がれた配信物からでも、画像から payload を抽出 → 外部の Manifest Repository に問い合わせ、という経路で来歴に辿り着けます。

実装

Trustmark の Q バリアントで、短い識別子 TT0001 を埋め込みます。

# embed_watermark.py
from PIL import Image
from trustmark import TrustMark

tm = TrustMark(verbose=False, model_type="Q")

payload = "TT0001"  # Q variant の payload は英数字 7文字程度が実用上の上限
cover = Image.open("input.jpg").convert("RGB")
encoded = tm.encode(cover, payload, MODE="text")
encoded.save("watermarked.png")
print(f"埋め込み完了: payload='{payload}' -> watermarked.png")

decoded, detected, version = tm.decode(Image.open("watermarked.png"), MODE="text")
print(f"抽出結果: detected={detected} version={version} payload='{decoded}'")

tm.decode()(payload, detected, version) の 3 要素を返します。detected は透かしの復号に成功したかを示す bool で、判定はこの bool 一発です(確信度のような連続値は出てきません)。初回実行時は Trustmark のモデルファイルのフェッチが走ります。

uv run python embed_watermark.py
# => 埋め込み完了: payload='TT0001' -> watermarked.png
# => 抽出結果: detected=True version=1 payload='TT0001'

detected=True で payload が一致していて、透かし自体はちゃんと成立しています。次はこの画像に C2PA Manifest を付与して、c2pa.soft-binding assertion として同じ payload を残していきます。

透かしを入れた事実をアクションとして残す

Manifest に payload を書くだけでは、実は片手落ちです。「この画像に不可視透かしを入れた」という編集操作そのものも、actions assertion として記録する必要があります。

C2PA 2.3 で透かし関連のアクションが細分化され、現在は次の 3 つが定義されています。

アクション意味
c2pa.watermarked2.2 で追加、2.3 で非推奨。claim generator は書いてはいけない
c2pa.watermarked.boundソフトバインディングを作る目的で不可視透かしを挿入した
c2pa.watermarked.unboundソフトバインディングを作らずに不可視透かしを挿入した

本記事のように payload を Manifest から引けるようにする場合は c2pa.watermarked.bound です。仕様書 Section 18.15.5 “Watermarking” は、このアクションと soft binding assertion をセットで置くことを求めています。

When using a c2pa.watermarked.bound action, a soft binding assertion shall also be included in the C2PA Manifest to describe the inserted watermark.

Guidance for Implementers Section 4.2.6 側はさらに踏み込んで、逆向き(透かしを入れたならアクションを書く)も must と書いています。

A c2pa.watermarked.bound action must be added to the C2PA Manifest to signal that an invisible watermark was inserted into the digital content for the purpose of creating a soft binding.

検証側にもこの対応関係のチェックが規定されていて、c2pa.watermarked.bound があるのに c2pa.soft-binding assertion が無ければ assertion.action.softBindingMissing で claim ごと reject されます。なお筆者が試した c2pa-rs 0.90.14 の時点では、このチェックはまだ実装されていないようで、アクションだけの Manifest でも検証は通りました。仕様に従っている限り困ることはありませんが、「検証が通ったから正しい」とは限らない例ではあります。

さらに C2PA 2.4 では、アクションから対応する assertion を parameters.relatedAssertions で名指しすることが推奨に加わりました。次のコードではこれも入れています。

c2pa-python で soft-binding assertion を記録する

# sign_soft_binding.py
import base64
import json

import c2pa

with open("test_cert.pem", "rb") as f:
    cert = f.read()
with open("test_key.pem", "rb") as f:
    key = f.read()

signer_info = c2pa.C2paSignerInfo(
    "es256", cert, key, b"http://timestamp.digicert.com"
)
signer = c2pa.Signer.from_info(signer_info)

payload = "TT0001"
payload_b64 = base64.b64encode(payload.encode("utf-8")).decode("ascii")

manifest_json = json.dumps({
    "title": "soft_binding_demo.png",
    "claim_generator_info": [{
        "name": "TechThanksSoftBindingDemo",
        "version": "0.1.0",
    }],
    "assertions": [
        {
            "label": "c2pa.actions.v2",
            "data": {
                "actions": [
                    {
                        "action": "c2pa.created",
                        "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture",
                    },
                    {
                        "action": "c2pa.watermarked.bound",
                        "softwareAgent": {"name": "Adobe Trustmark", "version": "0.9.1"},
                        "parameters": {
                            "relatedAssertions": [
                                {"url": "self#jumbf=c2pa.assertions/c2pa.soft-binding"}
                            ]
                        },
                    },
                ],
            },
            "created": True,
        },
        {
            "label": "c2pa.soft-binding",
            "data": {
                "alg": "com.adobe.trustmark.Q",
                "name": "Adobe Trustmark variant Q invisible watermark",
                "blocks": [
                    {
                        "scope": {},
                        "value": payload_b64,
                    }
                ],
            },
        },
    ],
})

builder = c2pa.Builder(manifest_json)
builder.sign_file("watermarked.png", "output.png", signer)
print("署名完了: output.png")
rm -f output.png
uv run python sign_soft_binding.py
# => 署名完了: output.png

c2patool output.png で Manifest を覗くと、Trustmark で埋め込んだ payload が c2pa.soft-binding としてきっちり記録されているのが見えます。

c2patool output.png | jq '.manifests[].assertions[] | select(.label == "c2pa.soft-binding")'
{
  "label": "c2pa.soft-binding",
  "data": {
    "alg": "com.adobe.trustmark.Q",
    "blocks": [
      {
        "scope": {},
        "value": "VFQwMDAx"
      }
    ],
    "name": "Adobe Trustmark variant Q invisible watermark"
  }
}

アクション側も見ておきます。

c2patool output.png | jq -c '.manifests[].assertions[] | select(.label == "c2pa.actions.v2") | .data.actions'
[
  {"action":"c2pa.created","digitalSourceType":"http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture"},
  {"action":"c2pa.watermarked.bound","softwareAgent":{"name":"Adobe Trustmark","version":"0.9.1"},"parameters":{"relatedAssertions":[{"url":"self#jumbf=c2pa.assertions/c2pa.soft-binding"}]}}
]

valueVFQwMDAx は Base64 で TT0001 を表します。

echo VFQwMDAx | base64 -d
# => TT0001

画像(透かし)と Manifest(soft-binding の value)で同じ識別子が持てていることが分かります。

余談: payload はハードコードでよいか

本記事のサンプルでは埋め込みと署名の両方で payload = "TT0001" をハードコードしていますが、設計としては「watermarked.png を Trustmark で decode し、その結果を c2pa.soft-binding の value に入れる」方が筋がいいと思っています。ソフトバインディングの本質は「画像から取り出せる payload に対して Manifest が紐づく」関係なので、画像側を真の値とみなす順序のほうが定義に沿います。実用上も、Trustmark の容量を超えた payload を渡すと黙って末尾が切られる挙動があるため、署名前に decode 結果と一致しているか確認しておくと不整合事故を防げます。一方、埋め込みと署名を同一プロセスで一気通貫に処理し、payload が容量内であることが事前にわかっているパイプラインなら、ハードコード(あるいは変数で持ち回す)でも問題ありません。Trustmark の decode は決して軽い処理ではない(モデル推論が走る)ため、整合性が確保されている前提なら省ける場面では省いた方が現実的、というのも実装上の判断としてあり得ます。ワークロードに応じて選んでいただければと思います。

検証側はアルゴリズム名をレジストリと照合する

ここで一度、署名した画像を検証にかけてみます。

c2patool output.png | jq -c '.validation_results.activeManifest.failure[] | {code, explanation}'
{"code":"signingCredential.untrusted","explanation":"signing certificate untrusted"}
{"code":"algorithm.unsupported","explanation":"soft binding algorithm 'com.adobe.trustmark.Q' is not in the C2PA soft binding algorithm registry"}

signingCredential.untrusted は c2pa-rs のテスト証明書を使っているので想定どおりですが、その下の algorithm.unsupported は初見だと戸惑います。com.adobe.trustmark.Q は 2024年5月に登録済み(identifier 4)で、レジストリにちゃんと載っているからです。

原因は c2pa-rs 側にあります。仕様書 Section 9.3.2 の「alg はレジストリに載った名前であるべき」という要件を c2pa-rs は実際に検証しているのですが、照合に使うリストは SDK に同梱されておらず、設定で渡す仕組みになっています。設定の型定義にも「List of soft binding algorithms to validate against. If not specified, soft binding errors may be generated.」とあり、未設定だと全部 unsupported 扱いです。

公式リストを取ってきて設定ファイルを作ります。

curl -sL -o softbinding-algorithm-list.json \
  https://raw.githubusercontent.com/c2pa-org/softbinding-algorithm-list/main/softbinding-algorithm-list.json

python3 -c '
import json
algs = [e["alg"] for e in json.load(open("softbinding-algorithm-list.json")) if not e.get("deprecated")]
json.dump({"version": 1, "soft_binding": {"soft_binding_algorithms": algs}}, open("c2pa-settings.json", "w"), indent=2)
print(f"{len(algs)} 件のアルゴリズムを設定に書き出しました")
'
# => 53 件のアルゴリズムを設定に書き出しました

version は数値です。文字列で "1.0.0" などと書くと invalid type: string "1.0.0", expected u32 で弾かれます。この設定を渡して再検証します。

c2patool --settings c2pa-settings.json output.png | jq -c '.validation_results.activeManifest.failure[] | {code, explanation}'
{"code":"signingCredential.untrusted","explanation":"signing certificate untrusted"}

algorithm.unsupported が消えました。

ちなみにこの照合は、assertion のラベルが c2pa.soft-binding のときだけ走ります。ハイフンをアンダースコアにして c2pa.soft_binding と書くと、署名も検証も何事もなく通ってしまいますが、それは正しいからではなく、c2pa-rs がその assertion をソフトバインディングとして認識していないからです。ラベルが違えば仕様上の soft binding assertion ではなく、ただのカスタム assertion として素通りします。検証が静かになる方向の間違いなので、気づきにくいのが厄介です。

Manifest Recovery をシミュレートする

ソフトバインディングの真価は「Manifest が剥がれてハードバインディングも通らなくなった画像から、来歴を引き直せる」ところにあります。ここでは SNS にアップロードされて JPEG に再エンコードされたような状況を再現して、その壊れた配信物から Manifest を復元するまでを通しでやってみます。

流れはこんな感じです。

  1. 元の output.png から Manifest を取り出し、c2pa.soft-binding の payload をキーに「Manifest Repository」へ登録する
  2. output.png を JPEG に再エンコードして、Manifest(JUMBF)を意図的に剥がす(tampered.jpg を作る)
  3. c2patool tampered.jpg で Manifest が検出できないことを確認する
  4. 透かしは画像ピクセルに焼かれているので、Trustmark で tampered.jpg から payload を抽出する
  5. 抽出した payload で Repository を引いて、元の Manifest を復元する

Manifest Repository は本番では Web API やキー値ストアになりますが、ここでは Python のグローバル dict で十分です。

# recover_manifest.py
import base64
import json
import subprocess

from PIL import Image
from trustmark import TrustMark


# --- 簡易 Manifest Repository(本番ではキー値ストアや Web API になる) ---
MANIFEST_REPOSITORY: dict[str, dict] = {}


def repository_register(payload: str, manifest: dict) -> None:
    MANIFEST_REPOSITORY[payload] = manifest


def repository_lookup(payload: str) -> dict | None:
    return MANIFEST_REPOSITORY.get(payload)


def read_manifest(path: str) -> dict | None:
    result = subprocess.run(
        ["c2patool", path], capture_output=True, text=True
    )
    if result.returncode != 0:
        return None
    return json.loads(result.stdout)


# --- 1) 元画像の Manifest を Repository に登録 ---
original = read_manifest("output.png")
assert original is not None, "output.png から Manifest を読めなかった"
active_id = original["active_manifest"]
sb = next(
    a for a in original["manifests"][active_id]["assertions"]
    if a["label"] == "c2pa.soft-binding"
)
payload_key = base64.b64decode(sb["data"]["blocks"][0]["value"]).decode("utf-8")
repository_register(payload_key, original["manifests"][active_id])
print(f"[1] Repository 登録: payload='{payload_key}'")

# --- 2) Manifest を剥がした画像を作る(JPEG 再エンコード) ---
Image.open("output.png").convert("RGB").save("tampered.jpg", "JPEG", quality=85)
print("[2] tampered.jpg を生成(JPEG 再エンコードで Manifest を除去)")

# --- 3) tampered.jpg に Manifest が残っていないことを確認 ---
tampered_manifest = read_manifest("tampered.jpg")
print(f"[3] tampered.jpg の Manifest: {'なし' if tampered_manifest is None else 'あり'}")

# --- 4) 画像から Trustmark で payload を抽出 ---
tm = TrustMark(verbose=False, model_type="Q")
extracted, detected, _ = tm.decode(Image.open("tampered.jpg"), MODE="text")
print(f"[4] tampered.jpg から payload を抽出: detected={detected} payload='{extracted}'")

# --- 5) Repository に問い合わせて Manifest を復元 ---
recovered = repository_lookup(extracted) if detected else None
if recovered is None:
    print("[5] NG: Repository から Manifest を復元できなかった")
else:
    print("[5] OK: Repository から Manifest を復元")
    print(f"    title           : {recovered['title']}")
    print(f"    claim_generator : {recovered['claim_generator_info'][0]['name']}")
    actions = next(
        a["data"]["actions"]
        for a in recovered["assertions"] if a["label"] == "c2pa.actions.v2"
    )
    print(f"    actions         : {[a['action'] for a in actions]}")
uv run python recover_manifest.py
# => [1] Repository 登録: payload='TT0001'
# => [2] tampered.jpg を生成(JPEG 再エンコードで Manifest を除去)
# => [3] tampered.jpg の Manifest: なし
# => [4] tampered.jpg から payload を抽出: detected=True payload='TT0001'
# => [5] OK: Repository から Manifest を復元
# =>     title           : soft_binding_demo.png
# =>     claim_generator : TechThanksSoftBindingDemo
# =>     actions         : ['c2pa.created', 'c2pa.watermarked.bound']

JPEG 再エンコードによって Manifest(JUMBF)は完全に剥がれて、c2patool では Error: No claim found になります。それでも画像側のピクセルに焼かれた透かしは生きていて、Trustmark で payload を取り出して Repository を引けば、元 Manifest の titleclaim_generatoractions までそのまま復元できる、という流れがそのまま動きます。「画像が単独で出回っても、透かしから来歴に辿り着ける経路が一本残る」というソフトバインディングの狙いを、コードの実行結果として手元で確認できる構成です。

本番環境では、この MANIFEST_REPOSITORY がキー値ストアや Web API に置き換わります。

まとめ

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

本記事では、Adobe Trustmark を使って画像に payload を焼き付け、c2pa-python から c2pa.soft-binding assertion と c2pa.watermarked.bound アクションの両方を Manifest に残すところまでを、アルゴリズムレジストリの照合設定と Manifest Recovery のシミュレーションも含めて手元で確認しました。assertion のラベルをハイフンにするか、アクションを添えるか、レジストリを設定に渡すかといった細部は、間違えても署名が止まらない代わりに検証側が静かに素通りする形で効いてくるので、仕様の CDDL と突き合わせておく価値があります。前編のハードバインディング実装 と合わせて読むと、両バインディングの守備範囲の違いがよりはっきり掴めるはずです。

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