メッセージダイジェスト(MD)、メッセージ認証コード(MAC)、鍵導出関数(KDF)の違いについてのメモ

「ハッシュ」という言葉があいまいに使われている場面をしばしば目にするので、関係する概念とそれらの違いについてまとめてみる。

メッセージダイジェスト(Message Digest; MD)

MD5SHA-1、SHA-256など、あるバイト列(メッセージ)に対し固定長の要約値を求めるアルゴリズム。 メッセージの同一性判定に用いられる。

同一の要約値となる(異なる)メッセージの組を求めることが計算量的に困難(強衝突耐性)なものをメッセージダイジェストと呼び、暗号学的ハッシュ関数とも呼ばれる。 同一の要約値となるメッセージの組が容易に求められるものは、誤り検出符号と呼ばれ区別される。 CRC32は誤り検出符号の一種である。

PythonでSHA-256を用いてメッセージダイジェストを求める例を次に示す。

$ python
Python 2.7.5 (default, Oct  2 2013, 22:34:09)
[GCC 4.8.1] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hashlib
>>> hashlib.sha256('foobar').hexdigest()
'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2'

メッセージ認証コード(Message Authentication Code; MAC

あるバイト列(メッセージ)に対し共通鍵を用いて固定長のバイト列を求めるアルゴリズム。 メッセージの改ざん検知に用いられる。

共通鍵を用いる点においてメッセージダイジェストと異なり、完全性を保護しメッセージの改ざんを防ぐことができる。 つまり、メッセージダイジェストではメッセージ改ざん後の値を容易に計算できるのに対し、メッセージ認証コードでは共通鍵がなければ計算できない。 また、デジタル署名とは公開鍵ではなく共通鍵を用いている点において異なり、否認防止の性質をもたない。 つまり、デジタル署名では公開鍵の持ち主がメッセージを生成したことが保証されるのに対し、メッセージ認証コードでは誰がメッセージを生成したかを特定することはできない。

メッセージ認証コードには、ハッシュ関数を用いる方式(HMAC)とブロック暗号アルゴリズムを用いる方式(CBC-MACなど)がある。 PythonでSHA-256を用いたHMACを求める例を次に示す。

$ python
Python 2.7.10 (default, Jun  1 2015, 18:05:38)
[GCC 4.9.2] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> import hashlib
>>> import hmac
>>> key = os.urandom(16)
>>> hmac.new(key, 'foobar', hashlib.sha256).hexdigest()
'05d531abfb693adcb3adbdc0cd3800404636fd1726ad851c86e61683cf33c7f0'

鍵導出関数(Key Derivation Function; KDF)

秘密のバイト列(マスター鍵)から固定長の新たな鍵となるバイト列を求める(導出する)アルゴリズム。 ブロック暗号アルゴリズムに用いるのに適した、固定長で一定のエントロピーを持つより安全な鍵を得ることができる。 また、パラメータに繰り返し回数などを取ることで計算量を調整できるようになっており、導出された鍵からマスター鍵を総当たりで求めることが難しくなっている。 このため、データベースに保存しておくためのパスワードの安全な変換に適している。

Python(2.7.8、3.4以降)でHMAC SHA-256を用いたPBKDF2により新たな鍵を求める例を次に示す。

$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> import hashlib
>>> import binascii
>>> salt = os.urandom(16)
>>> derived_key = hashlib.pbkdf2_hmac('sha256', b'foobar', salt, 100000)
>>> binascii.hexlify(derived_key)
b'e3fddc16c83da4ef7f6942f90111b2f129ec8b528a8ae8373ad89592285fab6c'