第5章で残した宿題

第5章では、こういう限界に突き当たりました。

誤字の修正も、権利表示の書き換えも、画像の差し替えも、同じ drifted になります。 分けたければ、署名の対象を分けるしかありません。

その「分ける」を実際にやってみます。ただし、手を動かすともっと悪い事実が出てきました。

マニフェストは画像の URI しか持っていない

IIIF のマニフェストが画像について書いているのは、こういう参照だけです。

"body": {
  "id": "https://…/iiif/genji/image/1/full/max/0/default.png",
  "type": "Image",
  "format": "image/png",
  "height": 400,
  "width": 640
}

中身のハッシュはどこにもありません。 ということは、

URI を変えずに、その先のバイト列だけを差し替えると、マニフェストのハッシュは1ビットも変わりません。

第5章までの検証は、これを堂々と valid と判定します。マニフェストは何も改竄されていないので、判定として間違ってはいません。ただ、保証していた範囲が記述だけだったというだけです。

資料としては最も重い改竄が、記述への署名では原理的に見えない。これは対象を分ける「ほうがよい」という話ではなく、分けなければそもそも扱えない、という話でした。

実体にも署名する

画像そのものに対して、もう1本の証明書を出します。載せるのは2種類のハッシュです。

[2] 実体に署名する
  image/1  バイト 252cf51125e8…  画素 2d40f31954e6…  640×400
  image/2  バイト 0706b3a528ea…  画素 ffb534a7c346…  640×400
何のハッシュか何が変わると壊れるか
バイト列ファイルの中身そのもの1バイトでも変われば壊れる。再圧縮でも壊れる
画素復号した後の画素の並び絵が変わったときだけ壊れる

この2つが割れる状態が、次章で扱う「フォーマット移行」の正体です。

4つのケース

記述と実体、2本の証明書がそれぞれどう判定するかを見ます。

ケース内容マニフェスト実体
Aそのままvalidvalid
B記述の誤字を直すdriftedvalid
C同じ URI のまま画像を差し替えるvalidinvalid
D再圧縮するvalidmigrated

C — 対象を分けなければならない理由

[C] 同じ URI のまま画像を差し替える — 別の絵。マニフェストは1ビットも変わらない
  マニフェストの VC  valid
  ✘ バイト列のハッシュ  署名時 252cf51125e8… / 手元 346236a5487f…
  ✘ 画素のハッシュ      署名時 2d40f31954e6… / 手元 0d7ce32d755b…
  実体の VC          invalid

マニフェストの側は valid のままです。 記述は本当に何も変わっていないので、これは正しい判定です。落ちるのは実体の側だけ。

分けていなければ、この改竄は検出できませんでした。

B と C が、これで初めて別々になる

第5章では、誤字の修正も画像の差し替えも同じ drifted でした。対象を2本に分けると、次のように割れます。

B(誤字の修正)      マニフェスト drifted   実体 valid
C(画像の差し替え)  マニフェスト valid     実体 invalid

「それが重大か」を機械が言えるようになったわけではありません。 対象を分けた結果、重大なものだけが invalid に落ちるようになった、というのが正確な言い方です。

判断の基準を人間が先に決めて、それを構造として埋め込んだ。機械が判断できるようになったのではなく、判断が設計に移ったわけです。第5章で「署名の対象を分けるしかない」と書いたのは、このことでした。

D — 5つ目の判定

[D] 再圧縮する — 同じ絵。圧縮率だけ変える = 可逆な移行
  マニフェストの VC  valid
  ✘ バイト列のハッシュ  署名時 252cf51125e8… / 手元 0c64e2f850d6…
  ✔ 画素のハッシュ      一致 2d40f31954e6…  (同じ絵・違うバイト列)
  実体の VC          migrated

バイト列は壊れ、画素は一致しています。 絵は変わっていないのに、ファイルは別物になった。

この「同じ絵・違うバイト列」が実際に起きるのは、次のような場合です。

操作画素起きること
PNG の圧縮率を変える(この試作でやったこと)一致zlib の設定が変わるだけ。展開すれば同じ画素
PNG のメタデータを足す・消す一致画素データに触らない
TIFF ⇄ PNG(どちらも可逆圧縮)一致容器が変わるだけ
JPEG → PNG一致JPEG を展開した結果をそのまま保存する場合
JPEG → WebP(非可逆)変わる再エンコードで量子化がやり直される
解像度を下げる / 色数を減らす変わる情報が落ちる

JPEG → WebP は、設定次第で両方に転びます。WebP は可逆・非可逆の両方を持つので、可逆モードなら画素は保たれ、既定の非可逆モードなら変わります。

そして厄介なのは、JPEG のような非可逆形式は「開いて保存し直す」だけで画素が変わりうることです。長期保存で TIFF や PNG のような可逆形式が選ばれるのは、移行のたびに中身が痩せていくのを避けるためでもあります。

境目は「フォーマットの違い」ではなく、その変換が可逆かどうかです。

ここに migrated という判定を置きました。invalid と言い切ると、書庫が圧縮方式を変えただけで全部の保証が無効になります。かといって valid にすると、バイト列に署名した意味が消える。

第5章の drifted を置いたときと、まったく同じ形の判断です。

判定は5つになった

判定意味どの層の話か
valid署名も署名者も内容も一致
drifted記述が別の版記述
migratedバイト列は違うが、中身は同じ実体
untrusted署名者を信じる根拠がない制度
invalid署名が通らない / 中身が違う

増え方に規則性があります。2値では扱えないものが出てくるたびに、状態が1つ増えています。

  • 資料は更新される → drifted
  • 誰でも発行者になれる → untrusted
  • ファイルは移行される → migrated

いずれも「拒否すると運用が回らないが、通すと意味が消える」という同じ形の板挟みで、そのたびに判断を利用者に返すという同じ解き方をしています。

この章で分かったこと

記述への署名は、実体を1バイトも保証していませんでした。 IIIF のマニフェストは記述と参照を1つの文書に混ぜているので、この事実は見落としやすい形になっています。

そして migrated が示したのは、バイト列の同一性は思ったより脆いということです。悪意がなくても、書庫が普通に運用しているだけで壊れます。次章はそこを扱います。