Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11224] ファイルの概要

このコミットは、Go言語の標準ライブラリ crypto/hmac パッケージにHMAC-SHA224、HMAC-SHA384、HMAC-SHA512のサポートを追加し、HMACの実装におけるハッシュ関数のブロックサイズに関する既存の制約を解消することを目的としています。

コミット

commit a5263c7caa61eb9eedfd6c15e3c1f989d5490ef9
Author: Luit van Drongelen <luitvd@gmail.com>
Date:   Wed Jan 18 10:36:28 2012 -0500

    crypto/hmac: Add HMAC-SHA224 and HMAC-SHA384/512
    
    First was, apart from adding tests, a single line of code (to add the
    constructor function). Adding SHA512-based hashing to crypto/hmac
    required minor rework of the package because of a previously hardcoded
    block-size in it's implementation. Instead of using a hash.Hash
    generator function the constructor function now uses a crypto.Hash
    type, which was extended to expose information about block size.
    
    The only standard library package impacted by the change is
    crypto/tls, for which the fix is included in this patch. It might be
    useful to extend gofix to include this API change too.
    
    R=agl, r, rsc, r
    CC=golang-dev
    https://golang.org/cl/5550043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a5263c7caa61eb9eedfd6c15e3c1f989d5490ef9

元コミット内容

crypto/hmac パッケージにHMAC-SHA224、HMAC-SHA384/512を追加する。 SHA224の追加は、テストの追加を除けばコンストラクタ関数の追加という単一のコード行で済んだ。しかし、SHA512ベースのハッシュ関数を crypto/hmac に追加するには、以前の実装でブロックサイズがハードコードされていたため、パッケージの小規模な手直しが必要だった。 以前は hash.Hash ジェネレータ関数を使用していたが、コンストラクタ関数は crypto.Hash 型を使用するように変更され、これによりブロックサイズに関する情報が公開されるようになった。 この変更によって影響を受ける唯一の標準ライブラリパッケージは crypto/tls であり、その修正もこのパッチに含まれている。このAPI変更を gofix に含めることも有用かもしれない。

変更の背景

このコミットの主な背景は、Go言語の標準ライブラリにおける暗号化機能の拡充と、より堅牢で汎用的なHMAC実装の実現です。具体的には以下の点が挙げられます。

  1. 新しいHMACアルゴリズムのサポート: SHA-2ファミリーのハッシュ関数であるSHA-224、SHA-384、SHA-512は、それぞれ異なるセキュリティ強度と出力長を持ち、特定のアプリケーション要件やセキュリティポリシーに対応するために重要です。これらHMACアルゴリズムのサポートを追加することで、Go言語の暗号ライブラリの機能が強化されます。
  2. HMAC実装の汎用性向上: 従来の crypto/hmac パッケージでは、HMACの内部処理で使用されるブロックサイズが64バイトにハードコードされていました。これはMD5やSHA-1といった一般的なハッシュ関数には適合しますが、SHA-512のようにブロックサイズが128バイトのハッシュ関数には対応できませんでした。このハードコードされた制約を解消し、ハッシュ関数が自身のブロックサイズを公開できるようにすることで、HMACの実装がより汎用的になり、将来的な新しいハッシュアルゴリズムの追加にも柔軟に対応できるようになります。
  3. crypto.Hash 型の活用: crypto.Hash 型は、Go言語の crypto パッケージ内で様々なハッシュアルゴリズムを抽象化するための列挙型です。この型をHMACのコンストラクタ関数で利用することで、ハッシュアルゴリズムの選択とブロックサイズの取得をより統一的かつ効率的に行えるようになります。

前提知識の解説

1. ハッシュ関数 (Hash Function)

ハッシュ関数は、任意の長さのデータを入力として受け取り、固定長の短いデータ(ハッシュ値、メッセージダイジェスト、フィンガープリントなどと呼ばれる)を出力する一方向性の関数です。主な特性は以下の通りです。

  • 一方向性: ハッシュ値から元のデータを復元することは非常に困難です。
  • 衝突耐性: 異なる入力から同じハッシュ値が生成されること(衝突)は非常に稀であるべきです。
  • 高速性: ハッシュ値の計算は高速に行われるべきです。

暗号学的ハッシュ関数は、データの完全性検証、デジタル署名、パスワードの保存など、様々なセキュリティ用途に利用されます。

2. SHA-2 (Secure Hash Algorithm 2)

SHA-2は、アメリカ国家安全保障局(NSA)によって設計された暗号学的ハッシュ関数のファミリーです。SHA-1の後継として開発され、より高いセキュリティレベルを提供します。SHA-2ファミリーには、主に以下のバリエーションがあります。

  • SHA-256: 256ビットのハッシュ値を生成します。ブロックサイズは64バイトです。
  • SHA-224: SHA-256の切り詰め版で、224ビットのハッシュ値を生成します。ブロックサイズは64バイトです。
  • SHA-512: 512ビットのハッシュ値を生成します。ブロックサイズは128バイトです。
  • SHA-384: SHA-512の切り詰め版で、384ビットのハッシュ値を生成します。ブロックサイズは128バイトです。
  • SHA-512/224, SHA-512/256: SHA-512の内部構造を使用しつつ、出力長を224ビットまたは256ビットに切り詰めたものです。

3. HMAC (Keyed-Hash Message Authentication Code)

HMACは、メッセージ認証コード(MAC)の一種で、共有秘密鍵と暗号学的ハッシュ関数を組み合わせてメッセージの完全性と認証性を保証するメカニズムです。HMACは、メッセージが改ざんされていないこと、およびメッセージが正当な送信者から送られたものであることを確認するために使用されます。

HMACの計算は、RFC 2104 (HMAC: Keyed-Hashing for Message Authentication) および FIPS 198 (The Keyed-Hash Message Authentication Code (HMAC)) で定義されており、以下の手順で行われます。

  1. 鍵の準備:
    • 秘密鍵 K がハッシュ関数のブロックサイズ B よりも長い場合、K をハッシュ関数でハッシュ化し、その結果を新しい鍵 K' とします。
    • 秘密鍵 K がブロックサイズ B よりも短い場合、K をゼロパディングしてブロックサイズ B にします。
    • 結果として得られる鍵を K' とします。
  2. 内部パディング (ipad): K'ipad (0x36をブロックサイズ分繰り返したバイト列) をXOR演算します。これを K_inner とします。
  3. 外部パディング (opad): K'opad (0x5cをブロックサイズ分繰り返したバイト列) をXOR演算します。これを K_outer とします。
  4. 内部ハッシュ: K_inner とメッセージ M を連結し、ハッシュ関数 H でハッシュ化します: H(K_inner || M)
  5. 外部ハッシュ: K_outer と内部ハッシュの結果を連結し、ハッシュ関数 H でハッシュ化します: H(K_outer || H(K_inner || M))。これが最終的なHMAC値となります。

HMACのセキュリティは、基盤となるハッシュ関数の強度と秘密鍵の秘匿性に依存します。

4. ハッシュ関数のブロックサイズ

ハッシュ関数は、入力データを固定長のブロックに分割し、各ブロックを順次処理していくことでハッシュ値を計算します。この固定長が「ブロックサイズ」です。例えば、MD5やSHA-1、SHA-256のブロックサイズは64バイトですが、SHA-512のブロックサイズは128バイトです。HMACの計算では、このブロックサイズが鍵のパディングや内部/外部ハッシュの計算に不可欠な要素となります。

5. hash.Hash インターフェースと crypto.Hash 型 (Go言語)

  • hash.Hash インターフェース: Go言語の hash パッケージで定義されているインターフェースで、すべてのハッシュ関数が実装すべき共通のメソッドを定義しています。これには Write (データをハッシュ関数に書き込む)、Sum (ハッシュ値を計算して返す)、Reset (ハッシュ関数の状態をリセットする)、Size (ハッシュ値のバイト長を返す) などが含まれます。
  • crypto.Hash: crypto パッケージで定義されている列挙型で、Go言語がサポートする特定の暗号学的ハッシュアルゴリズム(例: crypto.MD5, crypto.SHA1, crypto.SHA256 など)を表します。この型は、ハッシュアルゴリズムを識別し、それに対応する hash.Hash インターフェースを実装したインスタンスを生成するために使用されます。

技術的詳細

このコミットの技術的な核心は、crypto/hmac パッケージがハッシュ関数のブロックサイズを動的に取得できるように変更された点にあります。これにより、SHA-512のようにブロックサイズが異なるハッシュ関数でもHMACを正しく計算できるようになりました。

1. hash.Hash インターフェースへの BlockSize() メソッドの追加

最も重要な変更は、src/pkg/hash/hash.goBlockSize() int メソッドが追加されたことです。

// src/pkg/hash/hash.go
type Hash interface {
	// ... (既存のメソッド)

	// BlockSize returns the hash's underlying block size.
	// The Write method must be able to accept any amount
	// of data, but it may operate more efficiently if all writes
	// are a multiple of the block size.
	BlockSize() int
}

この変更により、hash.Hash インターフェースを実装するすべてのハッシュ関数は、自身のブロックサイズを返す BlockSize() メソッドを提供することが義務付けられました。

2. 各ハッシュ関数の BlockSize() 実装

crypto/md4, crypto/md5, crypto/ripemd160, crypto/sha1, crypto/sha256, crypto/sha512 など、Go標準ライブラリ内の既存のハッシュ関数実装に、それぞれのブロックサイズを返す BlockSize() メソッドが追加されました。

例えば、crypto/sha512/sha512.go では BlockSize = 128 が定義され、BlockSize() メソッドがこれを返します。

// src/pkg/crypto/sha512/sha512.go
const Size = 64
const Size384 = 48

// The blocksize of SHA512 and SHA384 in bytes.
const BlockSize = 128 // <-- 新しく追加された定数

// ...

func (d *digest) BlockSize() int { return BlockSize } // <-- 新しく追加されたメソッド

同様に、SHA-256やMD5などブロックサイズが64バイトのハッシュ関数には BlockSize = 64 が追加され、BlockSize() メソッドがそれを返すように変更されました。

3. crypto/hmac パッケージの変更

crypto/hmac パッケージは、この新しい BlockSize() メソッドを利用するように修正されました。

  • padSize 定数の削除と blocksize フィールドの追加: 以前は padSize = 64 というハードコードされた定数を使用していましたが、これが削除され、hmac 構造体に blocksize int フィールドが追加されました。

    // src/pkg/crypto/hmac/hmac.go
    type hmac struct {
    	size         int
    	blocksize    int // <-- 新しく追加されたフィールド
    	key, tmp     []byte
    	outer, inner hash.Hash
    }
    
  • New 関数の変更: hmac.New 関数は、ハッシュ関数を生成する func() hash.Hash 型の引数を受け取っていましたが、このコミットでは crypto.Hash 型の引数を受け取るように変更されたとコミットメッセージに記載されています。しかし、実際のコード変更を見ると、New 関数のシグネチャは func New(h func() hash.Hash, key []byte) hash.Hash のままで、内部で hm.inner.BlockSize() を呼び出すように変更されています。コミットメッセージの「constructor function now uses a crypto.Hash type」という記述は、おそらく crypto.Hash 型から hash.Hash インターフェースを実装した具体的なハッシュ関数インスタンスを取得する内部ロジックの変更を指しているか、あるいは将来的な変更の意図を示唆している可能性があります。

    重要なのは、hm.blocksize = hm.inner.BlockSize() の行が追加され、HMACインスタンスが内部ハッシュ関数の実際のブロックサイズを取得して保持するようになった点です。

    // src/pkg/crypto/hmac/hmac.go
    // New returns a new HMAC hash using the given hash generator and key.
    func New(h func() hash.Hash, key []byte) hash.Hash {
    	hm := new(hmac)
    	hm.outer = h()
    	hm.inner = h()
    	hm.size = hm.inner.Size()
    	hm.blocksize = hm.inner.BlockSize() // <-- ここでハッシュ関数のブロックサイズを取得
    	hm.tmp = make([]byte, hm.blocksize+hm.size) // <-- blocksizeを使用
    	if len(key) > hm.blocksize { // <-- blocksizeを使用
    		// If key is too big, hash it.
    		hm.outer.Write(key)
    		key = hm.outer.Sum(nil)
    	}
    	// ...
    }
    
  • パディング処理の変更: tmpPad 関数や Sum 関数内のパディング処理において、ハードコードされていた padSize の代わりに h.blocksize フィールドが使用されるようになりました。

    // src/pkg/crypto/hmac/hmac.go
    func (h *hmac) tmpPad(xor byte) {
    	for i, k := range h.key {
    		h.tmp[i] = xor ^ k
    	}
    	for i := len(h.key); i < h.blocksize; i++ { // <-- h.blocksizeを使用
    		h.tmp[i] = xor
    	}
    }
    
    func (h *hmac) Sum(in []byte) []byte {
    	// ...
    	copy(h.tmp[h.blocksize:], in[origLen:]) // <-- h.blocksizeを使用
    	// ...
    }
    
    func (h *hmac) Reset() {
    	h.inner.Reset()
    	h.tmpPad(0x36)
    	h.inner.Write(h.tmp[0:h.blocksize]) // <-- h.blocksizeを使用
    }
    

4. crypto/tls の修正

コミットメッセージにある通り、crypto/tls パッケージもこの変更の影響を受けました。これは、crypto/tls がHMACを使用する際に、HMACの内部構造の変更に適応する必要があったためです。パッチには src/pkg/exp/ssh/transport.go の変更が含まれており、truncatingMAC 構造体に BlockSize() メソッドが追加され、内部のHMACインスタンスの BlockSize() を呼び出すように変更されています。

// src/pkg/exp/ssh/transport.go
func (t truncatingMAC) Size() int {
	return t.length
}

func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } // <-- 新しく追加されたメソッド

5. テストの追加

src/pkg/crypto/hmac/hmac_test.go には、SHA-224、SHA-384、SHA-512を含む新しいHMACテストケースが多数追加されました。これらのテストは、NISTのHMACテストベクトル(http://csrc.nist.gov/groups/ST/toolkit/examples.html など)に基づいています。これにより、新しいアルゴリズムが正しく実装され、既存のアルゴリズムも変更後も正しく機能することが保証されます。

コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。

  1. src/pkg/hash/hash.go:
    • Hash インターフェースに BlockSize() int メソッドが追加されました。
  2. src/pkg/crypto/hmac/hmac.go:
    • hmac 構造体に blocksize int フィールドが追加されました。
    • New 関数内で、ハッシュ関数の BlockSize() メソッドを呼び出して hmac 構造体の blocksize フィールドを初期化するようになりました。
    • HMACの内部処理(パディング、Sum、Resetなど)で、ハードコードされていた padSize 定数の代わりに h.blocksize フィールドが使用されるようになりました。
  3. src/pkg/crypto/hmac/hmac_test.go:
    • hmacTest 構造体の hash フィールドの型が func([]byte) hash.Hash から func() hash.Hash に変更されました。
    • TestHMAC 関数内で、NewSHA1, NewMD5, NewSHA256 といったHMACパッケージ内のコンストラクタ関数ではなく、sha1.New, md5.New, sha256.New といった各ハッシュパッケージのコンストラクタ関数を直接使用するように変更されました。
    • HMAC-SHA224, HMAC-SHA384, HMAC-SHA512の新しいテストケースが多数追加されました。
  4. 各ハッシュ関数実装ファイル (src/pkg/crypto/md4/md4.go, src/pkg/crypto/md5/md5.go, src/pkg/crypto/ripemd160/ripemd160.go, src/pkg/crypto/sha1/sha1.go, src/pkg/crypto/sha256/sha256.go, src/pkg/crypto/sha512/sha512.go, src/pkg/hash/adler32/adler32.go, src/pkg/hash/crc32/crc32.go, src/pkg/hash/crc64/crc64.go, src/pkg/hash/fnv/fnv.go):
    • それぞれのハッシュ関数の BlockSize 定数と BlockSize() メソッドが追加されました。
  5. src/pkg/exp/ssh/transport.go:
    • truncatingMAC 構造体に BlockSize() メソッドが追加され、内部のHMACインスタンスの BlockSize() を呼び出すように変更されました。
  6. src/pkg/crypto/openpgp/canonical_text.gosrc/pkg/crypto/openpgp/canonical_text_test.go:
    • canonicalTextHash 構造体にも BlockSize() メソッドが追加され、内部のハッシュ関数の BlockSize() を呼び出すように変更されました。テストファイルにも対応する変更が加えられました。

コアとなるコードの解説

src/pkg/hash/hash.go の変更

Hash インターフェースに BlockSize() int が追加されたことで、Go言語のハッシュ関数は、そのアルゴリズムが処理するブロックのサイズを外部に公開できるようになりました。これは、HMACのようなハッシュ関数のブロックサイズに依存するアルゴリズムにとって極めて重要です。以前は、HMACの実装が特定のブロックサイズ(例: 64バイト)を仮定していましたが、この変更により、HMACは使用するハッシュ関数から直接正しいブロックサイズを取得できるようになり、汎用性と正確性が向上しました。

src/pkg/crypto/hmac/hmac.go の変更

  • hmac 構造体の blocksize フィールド: このフィールドは、HMACインスタンスが使用する基盤となるハッシュ関数のブロックサイズを動的に保持するために導入されました。これにより、HMACの内部計算(鍵のパディングや内部/外部ハッシュの準備)が、使用するハッシュ関数に依存して適切に行われるようになります。
  • New 関数での hm.inner.BlockSize() の利用: New 関数内で hm.blocksize = hm.inner.BlockSize() が呼び出されることで、HMACインスタンスが初期化される際に、実際に使用されるハッシュ関数(hm.inner)からそのブロックサイズが取得されます。これにより、MD5やSHA-1(64バイトブロック)だけでなく、SHA-512(128バイトブロック)のような異なるブロックサイズを持つハッシュ関数でも、HMACが正しく動作するようになります。
  • パディングロジックの汎用化: tmpPad, Sum, Reset 関数内で padSize 定数が h.blocksize に置き換えられたことで、HMACの内部パディング処理がハッシュ関数のブロックサイズに動的に適応するようになりました。これは、HMACの定義(RFC 2104)に厳密に従うために不可欠な変更です。鍵のパディングや中間ハッシュ値の結合において、正しいブロックサイズを使用することで、HMACのセキュリティと互換性が保証されます。

src/pkg/crypto/hmac/hmac_test.go の変更

テストコードの変更は、主に新しいHMACアルゴリズム(SHA224, SHA384, SHA512)の追加と、HMACのコンストラクタへのハッシュ関数インスタンスの渡し方の変更を反映しています。

  • hmacTest 構造体の hash フィールドの型変更は、テストケース内でハッシュ関数を生成する際に、HMACパッケージ内のラッパー関数ではなく、各ハッシュパッケージの標準コンストラクタ(例: sha1.New)を直接使用できるようにするためのものです。これにより、テストの記述がより直接的になり、Goの標準的なハッシュ関数利用パターンに沿う形になりました。
  • 新しいテストケースの追加は、このコミットで追加されたHMACアルゴリズムが、既知のテストベクトルに対して正しい出力を生成することを確認するために不可欠です。これにより、実装の正確性が検証され、将来的な回帰を防ぐための安全網が提供されます。

各ハッシュ関数実装ファイルへの BlockSize() の追加

各ハッシュ関数が自身の BlockSize() を実装したことで、Goのハッシュエコシステム全体がより統一的で情報豊富なものになりました。これにより、HMACのようなハッシュ関数の特性に依存する上位レベルの暗号プリミティブが、より堅牢かつ柔軟に構築できるようになります。

src/pkg/exp/ssh/transport.go および src/pkg/crypto/openpgp/canonical_text.go の変更

これらのファイルへの BlockSize() メソッドの追加は、Goの標準ライブラリ内でHMACやその他のハッシュ関数を利用している他のパッケージが、新しい hash.Hash インターフェースの変更に適応するためのものです。これにより、ライブラリ全体の一貫性が保たれ、依存関係が正しく解決されます。

関連リンク

参考にした情報源リンク