SHA-1衝突とsubversionもう少し

昨日の記事については特に誰も教えてくれなかったが、subversionのメールスレッドは自力で発見した。

http://mail-archives.apache.org/mod_mbox/subversion-dev/201702.mbox/%3C20170223200227.ynjumirjnmlesy5m%40sunbase.org%3E

これによると、Subversionのバックエンドでrep-sharingという機能がオンかオフかで判断されていて、デフォルトはオン、オフだと大丈夫、ということっぽい?

ただ、試してみると、shattered-1.pdfとshattered-2.pdfの内容は完全に一致してしまうため、変なバグは残っているような気がする。なんでこうなってるんだろうな。内容はshattered-1.pdfになっている。が、エラーにはならない、という意味ではマシ。


さて、rep-sharingがどういう機能なのかというと、 http://svnbook.red-bean.com/en/1.6/svn.reposadmin.maint.html によれば、

Repositories created with Subversion 1.6 or later further enjoy the disk space savings afforded by representation sharing, a feature which allows multiple files or file revisions with identical file content to refer to a single shared instance of that data rather than each having their own distinct copy thereof.

ということなので、コンテントのハッシュを計算し、ハッシュ値が一致した場合は片方だけを保存しておくということなのだろう。一方、コミット時のMD5およびSHA1の値は、メタデータとして別個に保存しておき、svn co時に使われる、ということなのだろうか?

ところで、昨日の記事には間違いがあった(訂正済み)。エラーメッセージを再掲すると、

$ svn co file:///tmp/svntest/repo2
A repo2/shattered-1.pdf
svn: E200014: Checksum mismatch for '/tmp/svntest/c2/repo2/shattered-2.pdf':
 expected: 5bd9d8cabc46041579a311230539b8d1
 actual: ee4aa52b139d925f8d8884402b0a750c

となっているのだけど、expectedはshattered-2.pdfの正しいMD5値、actualはshattered-1.pdfのMD5値になっている。つまり、チェックアウト時にコンテントのMD5値も検証していて、この不一致がエラーになっている。

コードはちゃんと読めていないけれど、ここから推測するに

  • svn coの際、コンテントのハッシュを検証する。SHA1とMD5の両方で検証されるか、MD5のみ検証するかどっちか
  • rep-sharingが有効な場合、レポジトリ内にはshattered-1.pdfの内容だけが保存されている。svn coではこの内容が取り出される
  • 一方、rep-sharingが有効な場合、メタデータのMD5とSHA1はキャッシュされているので、この内容を使って検証される
  • MD5値は一致しないのでエラー

といった流れになると思われる。

rep-sharingが有効でない場合でも、チェックアウトするとなぜか両方ともshattered-1.pdfの内容になってしまうのだけど、これはプロトコルが何らかの最適化により、同じSHA1値のものを取得するのを避けているからだろうか? よくわからないな……。

なおsvnadmin dumpの内容をよく見ると、

  • rep-sharingが有効な場合、dump内容ではshattered-1.pdfとshattered-2.pdfの内容は一致している(が、メタデータのMD5が異なる)
  • rep-sharingが無効な場合、dump時にはファイルの中身は異なる

となっていて、rep-sharingの機能が窺い知れる。

なおダンプ内容にも元々のMD5とSHA1の値が記録されるけれど、rep-sharingによってコンテントのハッシュは一致しないので、このダンプ結果をsvnadmin loadすると同じ検証エラーにより失敗することになる。