目次
HMAC (RFC 2104) を Python で実装しました。 ハッシュ関数には ライブラリ にある SHA-256 を使いました。
HMAC の概要
HMAC は、メッセージの送信者の認証とメッセージの改ざんの検出に用いられるメッセージ認証符号(MAC: Message Authentication Code)を生成するための暗号化技術です。 HMACは、ハッシュ関数と秘密鍵を組み合わせて、MACを求めます。
HMACは、以下のような特徴を持ちます。
- 秘密鍵とメッセージの両方に依存してMACを生成するので、秘密鍵を知らない者は同じMACを計算できません。これにより、送信者の認証が可能になります。
- ハッシュ関数を2回適用することで、メッセージの一部を変更してもMACが一致する確率を非常に低くします。これにより、メッセージの改ざんを検知する能力を高めます。
- HMACは、任意のハッシュ関数を用いることができます。例えば、SHA-256やMD5などのハッシュ関数をHMACに適用することができます。
- HMACは、不可逆な暗号化技術です。
手続の説明
RFC 2104 に丁寧に手続きが記載されています。
HMAC に必要なのは、 ハッシュ関数 H と 秘密鍵 K です。
ハッシュ関数 H のブロック長を B バイト, 出力長を L バイト と表すことにします。 ハッシュ関数 H = SHA-256 のときは、 B = 64, L = 32 です。
K は秘密鍵とありますが、 公開鍵暗号における秘密鍵である必要はなく、 他の人に知られることがない秘密の文字列であればOKです。 RFC 2104 では private key ではなく secret key と書かれています。 K の長さは、 RFC では、最低でもハッシュ関数の出力長と記載されています。 K はハッシュ値とのビット演算を行うものであるため、 ハッシュ値より短いと規定の値で埋める必要があり、推測しやすくなるからですね。
HMAC の計算の流れは次のとおりです。
- 秘密鍵 K が ハッシュ関数のブロック長 B よりも長い場合は、 ハッシュ関数によって K を L バイト のハッシュ値に変換します。 生成されたハッシュ値を HMAC で使用する秘密鍵 K とします。
- 秘密鍵 K が ハッシュ関数のブロック長 B よりも短い場合は、 全体で B バイト になるように、 K の末尾に ゼロ (
0x00
) を追加します。 - 上の計算結果と 0x36 を B 回 繰り返した値 (
ipad = b'\x36' * B
) の、 XOR を計算します。 ビット単位の演算です。 - 上の計算結果の後ろに 暗号化したいメッセージ (ビット配列) を加えます。
- 上の計算結果を ハッシュ関数 H によってハッシュ値に変換します。
- 秘密鍵と、 0x5c を B 回 繰り返した値 (
opad = b'\x5c' * B
) の XOR を計算します。 ビット単位の演算です。 - 計算されたハッシュ値を上の計算で得られたXORの後ろに追加します。
- 上の計算結果を ハッシュ関数 H を使ってハッシュ値に変換します。
プログラム
そしてこちらがPythonで記述した HMAC (SHA-256) です。
環境
- Python 3.10.13
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# HMAC with SHA-256 def sha256(message): # SHA-256 implementation import hashlib return hashlib.sha256(message).digest() def xor(a, b): # XOR two byte strings return bytes([x ^ y for x, y in zip(a, b)]) def hmac_sha256(key, message): """ HMAC-SHA256 implementation >>> text = b"Hi There" >>> key = b'\x0b' * 20 >>> a = hmac_sha256(key, text).hex() >>> import hmac >>> b = hmac.new(key, text.encode('ascii'), hashlib.sha256).digest().hex() >>> a == b True >>> text = b"Hello" * 100 >>> key = b'1234567890' * 100 >>> a = hmac_sha256(key, text).hex() >>> import hmac >>> b = hmac.new(key, text.encode('ascii'), hashlib.sha256).digest().hex() >>> a == b True :param key: :param message: :return: """ if len(key) > 64: key = sha256(key) if len(key) < 64: key = key + b'\x00' * (64 - len(key)) o_key_pad = xor(b'\x5c' * 64, key) i_key_pad = xor(b'\x36' * 64, key) return sha256(o_key_pad + sha256(i_key_pad + message)) if __name__ == '__main__': import hmac import hashlib |
HMAC に代わる他のMAC生成方法
HMAC は ハッシュ関数 と 秘密鍵 を組み合わせて MAC を求める方法でした。 MAC を生成する他の方法に、 Keccakハッシュ関数を使用する KMAC, 任意のブロック暗号を使用する CMAC があります。