元々は予定していなかったSHA関連ネタですが、折角なので残しておきます。
SHAの仕様が記述されているお馴染みのFIPS 180-4には、SHA-512/tというSHA-512をベースに、SHA-512の出力を任意のtビットに切り詰めるハッシュ関数についての記述があります。
その中にSHA-512/tのIV(初期化ベクトル、initialization vector)の生成方法も記述されていますが、いまいちピンとこなかったので調査を行いました。
その調査の結果を、備忘録代わりに残しておきます。
これを読んだあなたは、384を除く任意のtビット(t<512)のハッシュ関数が作れるようになるはずです(例えば、SHA-512/448とか)。
FIPS 180-4に記述のある初期化ベクトル生成方法は以下のようになっています。
また、SHA-512/224とSHA-512/256という承認されたハッシュアルゴリズムの初期化ベクトルについても記述があります(他のアルゴリズムは必要性が出てきたら明記されるみたいです)。
SHA-512/t IV Generation Functionは、要は、SHA-512の初期化ベクトル8つそれぞれと0xa5a5a5a5a5a5a5a5で排他的論理和を取った値を初期化ベクトルとして持つSHA-512に対して、新しく追加したいtを文字列として含む(例えば、”SHA-512/448″)文字列のメッセージダイジェスト(512bit)を出力させ、それらを64bitごとに8つに分割し、SHA-512/448の8つの初期化ベクトルとして使用するということが記述されています。
・・・。
ちょっと何言っているか分かりませんね。
それでは順番に見ていきます。
1.SHA-512の初期化ベクトルとの排他的論理和を得る
SHA-512の8つの初期化ベクトルと0xa5a5a5a5a5a5a5a5との排他的論理和を取って、出力させた結果は以下の通りです。
$ ./main i=[0], h0=0x6a09e667f3bcc908, xor=0xcfac43c256196cad i=[1], h1=0xbb67ae8584caa73b, xor=0x1ec20b20216f029e i=[2], h2=0x3c6ef372fe94f82b, xor=0x99cb56d75b315d8e i=[3], h3=0xa54ff53a5f1d36f1, xor=0xea509ffab89354 i=[4], h4=0x510e527fade682d1, xor=0xf4abf7da08432774 i=[5], h5=0x9b05688c2b3e6c1f, xor=0x3ea0cd298e9bc9ba i=[6], h6=0x1f83d9abfb41bd6b, xor=0xba267c0e5ee418ce i=[7], h7=0x5be0cd19137e2179, xor=0xfe4568bcb6db84dc
2.排他的論理和の結果を初期化ベクトルとして持つSHA-512の作成
GitHubから取ってきたopensslのソースコードを変更して、ビルドします。以下ではsha/という作業ディレクトリを作成し、その配下で行っています。
$ mkdir sha $ cd sha $ git clone https://github.com/openssl/openssl.git $ cd openssl/crypto/sha/ $ vi sha512.c
SHA512_Init関数を以下のように変更します。
int SHA512_Init(SHA512_CTX *c) { /* c->h[0] = U64(0x6a09e667f3bcc908); c->h[1] = U64(0xbb67ae8584caa73b); c->h[2] = U64(0x3c6ef372fe94f82b); c->h[3] = U64(0xa54ff53a5f1d36f1); c->h[4] = U64(0x510e527fade682d1); c->h[5] = U64(0x9b05688c2b3e6c1f); c->h[6] = U64(0x1f83d9abfb41bd6b); c->h[7] = U64(0x5be0cd19137e2179); */ c->h[0] = U64(0xcfac43c256196cad); c->h[1] = U64(0x1ec20b20216f029e); c->h[2] = U64(0x99cb56d75b315d8e); c->h[3] = U64(0xea509ffab89354); c->h[4] = U64(0xf4abf7da08432774); c->h[5] = U64(0x3ea0cd298e9bc9ba); c->h[6] = U64(0xba267c0e5ee418ce); c->h[7] = U64(0xfe4568bcb6db84dc); c->Nl = 0; c->Nh = 0; c->num = 0; c->md_len = SHA512_DIGEST_LENGTH; return 1; }
ビルドします。(コンパイラや依存ライブラリ等が必要な場合は適宜入れてください。)
$ cd ../../ $ ./config $ make $ cd ..
3.メッセージダイジェストの出力
2で作成したSHA-512に対して、”SHA-512/448″という文字列のメッセージダイジェストを出力させるため、以下のようなプログラム(適当ですみません。)を作成します。
$ vi test.c #include <stdio.h> #include <string.h> #include <openssl/sha.h> int main(int argc, char *argv[]) { unsigned char data[] = "SHA-512/448"; unsigned char digest[SHA512_DIGEST_LENGTH]; unsigned char md[SHA512_DIGEST_LENGTH*2+1]; int i; SHA512((unsigned char*)&data, strlen(data), (unsigned char*)&digest); for (i = 0; i < SHA512_DIGEST_LENGTH; i++) { sprintf(&md[i*2], "%02x", (unsigned int)digest[i]); } printf("SHA512 digest: %s\n", md); return 0; }
作成したtest.cを、2で作成したスタティックライブラリを使って、コンパイル、リンクします。
$ gcc -o test -I ./openssl/include/ test.c ./openssl/libcrypto.a
実行します。
$ ./test SHA512 digest: 8bdbdbb57b33f2bb54b6d1373964ccb9044d2d381ae3eb763d40619ccc846c46190000505524b786ae6afff7d04167eb12bdbffddbf2e23071a770ebff1126ee
出力された、”8bdbdbb….1126ee”がメッセージダイジェストです。
これを64bitごと(16文字ずつ)に分割し、8つの初期化ベクトルとして使用すれば、SHA-512/tの仕様に合った、SHA-512/448を作成することができます。
今回適当に例に挙げたSHA-512/448ですが、将来NIST(アメリカ国立標準技術研究所)がSHA-512/448が必要だと判断した場合、その初期化ベクトルは、以下の内容と一致するはずです。
H0 = 8bdbdbb57b33f2bb H1 = 54b6d1373964ccb9 H2 = 044d2d381ae3eb76 H3 = 3d40619ccc846c46 H4 = 190000505524b786 H5 = ae6afff7d04167eb H6 = 12bdbffddbf2e230 H7 = 71a770ebff1126ee
念のため、FIPS 180-4に記述されているSHA-512/224とSHA-512/256の値と一致するか確かめておきます。
SHA-512/224
$ ./test SHA512 digest: 8c3d37c819544da273e1996689dcd4d61dfab7ae32ff9c82679dd514582f9fcf0f6d2b697bd44da877e36f7304c489423f9d85a86a1d36c81112e6ad91d692a1
64bitごとに8つに分けると、
H0 = 8c3d37c819544da2 H1 = 73e1996689dcd4d6 H2 = 1dfab7ae32ff9c82 H3 = 679dd514582f9fcf H4 = 0f6d2b697bd44da8 H5 = 77e36f7304c48942 H6 = 3f9d85a86a1d36c8 H7 = 1112e6ad91d692a1
SHA-512/256
$ ./test SHA512 digest: 22312194fc2bf72c9f555fa3c84c64c22393b86b6f53b151963877195940eabd96283ee2a88effe3be5e1e25538639922b0199fc2c85b8aa0eb72ddc81c52ca2
64bitごとに8つに分けると、
H0 = 22312194fc2bf72c H1 = 9f555fa3c84c64c2 H2 = 2393b86b6f53b151 H3 = 963877195940eabd H4 = 96283ee2a88effe3 H5 = be5e1e2553863992 H6 = 2b0199fc2c85b8aa H7 = 0eb72ddc81c52ca2
どちらの場合も大文字小文字の違いはあれど、値は完全に一致しているので、SHA-512/t IV Generation Functionの理解は、問題なさそうです。
あとは、SHA-512のメッセージダイジェストを448bitに切り詰める処理を書けば、SHA-512/tの仕様に準拠するSHA-512/448が作成できます。
この内容を読んでやってみようという気になった人は、是非試してみてください。