第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 | そのまま | valid | valid |
| B | 記述の誤字を直す | drifted | valid |
| C | 同じ URI のまま画像を差し替える | valid | invalid |
| D | 再圧縮する | valid | migrated |
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 が示したのは、バイト列の同一性は思ったより脆いということです。悪意がなくても、書庫が普通に運用しているだけで壊れます。次章はそこを扱います。