引用元:https://www.ioccc.org/2019/diels-grabsch2/prog.c
審査員・作者による説明:https://www.ioccc.org/2019/diels-grabsch2/hint.html
動作
自分自身のSHA-512を出力する。
$ gcc -o prog prog.c
$ ./prog
43bd0c4381a5078d2850fca9ae5e0647596bcc03cd67d8d973ad02ff35c316c728e7b347ca70abe6c74e745e63646cc7643cb0cffcd3d9a969cbf31a7ce5bf68
$ sha512sum prog.c
43bd0c4381a5078d2850fca9ae5e0647596bcc03cd67d8d973ad02ff35c316c728e7b347ca70abe6c74e745e63646cc7643cb0cffcd3d9a969cbf31a7ce5bf68 prog.c
解説
コードのSHA-512を計算して単純にコード自身に埋め込むと、SHA-512が変わってしまうので、一見するとむずかしい問題設定。
そのため、2^512の空間をブルートフォースして作ったとか、名無しの権兵衛が作ったSHA-512衝突アルゴリズムを使ったなどと冗談が書いてある。
これは本質的にはQuine。
Quineのテクニックによって自分自身のソースコードを再構築し、そのまま出力する代わりにそのSHA-512を計算して出力する。
ただしだいぶ発展的な形になっている。
まず、最下行の先頭の"
までのSHA-512計算を事前にやっておき、計算状態をダンプする。
このダンプが、最下行の文字列リテラルにエンコードされて含まれている。
このプログラムはダンプされた計算状態から計算を再開し、最後の文字列リテラルの中身と、終端の"
;
\n
の3文字分を進めて、最終的なSHA-512を出力するようになっているとのこと。
フルのSHA-512を実装するのではなく、1ブロック分の計算に特化している。
苦労した点としては、SHA-512を計算するための定数が80個あること。
これをコードにもたせると巨大すぎるが、これらの英数は最初の80個の素数の3乗根を元に作られているので、自力で計算することにした。
ただし64ビットの精度で求める必要があり、double
型は52か53ビット程度しか精度がないので使えない。
また、32ビット環境でも動くようにしたいということもあり、24ビットずつ3つの整数に分割して計算したとのこと。
Quineを使うという基本的なアイデアに留まらず、この課題に特化して作りこんであり、結果的に簡潔にまとまっていてとても好印象。