SHA-512/tのIV(初期化ベクトル)生成方法

元々は予定していなかった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に記述のある初期化ベクトル生成方法は以下のようになっています。
IV generation function

また、SHA-512/224とSHA-512/256という承認されたハッシュアルゴリズムの初期化ベクトルについても記述があります(他のアルゴリズムは必要性が出てきたら明記されるみたいです)。
SHA-512_224_IV
SHA-512_256_IV

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が作成できます。
この内容を読んでやってみようという気になった人は、是非試してみてください。
 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*