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

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

このコミットは、Go言語の標準ライブラリ crypto/hmac パッケージに Equal 関数を追加するものです。この関数は、HMAC(Keyed-Hash Message Authentication Code)の値を比較する際に、タイミング攻撃(timing side-channel attacks)を防ぐために定数時間(constant time)で比較を行うことを目的としています。

コミット

commit 6720997f9e2a4371a5f2fc79b356e1610160139d
Author: Adam Langley <agl@golang.org>
Date:   Thu Oct 11 15:28:02 2012 -0400

    crypto/hmac: add Equal function.
    
    It was suggested that it's too easy to use crypto/hmac insecurely and
    I think that has some merit. This change adds a Equal function to
    make it obvious that MAC values should be compared in constant time.
    
    R=rsc, max
    CC=golang-dev
    https://golang.org/cl/6632044

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

https://github.com/golang/go/commit/6720997f9e2a4371a5f2fc79b356e1610160139d

元コミット内容

crypto/hmac: add Equal function.

このコミットは、crypto/hmac パッケージに Equal 関数を追加します。この変更は、crypto/hmac を安全でない方法で使用することが容易であるという指摘があり、その指摘には一理あると考えられたためです。Equal 関数を追加することで、MAC(Message Authentication Code)の値が定数時間で比較されるべきであることを明確にします。

変更の背景

HMACはメッセージの認証に使用される暗号学的ハッシュ関数であり、メッセージの完全性と認証性を保証します。しかし、HMACの値を検証する際に、単純なバイトごとの比較(例: bytes.Equal)を使用すると、比較にかかる時間が入力によって変動する可能性があります。この時間の変動は「タイミング攻撃」として知られるサイドチャネル攻撃の脆弱性を生み出す可能性があります。

攻撃者は、比較にかかる時間のわずかな違いを測定することで、MAC値のどのバイトが正しいかを推測し、最終的にMAC全体を再構築できる可能性があります。これにより、メッセージの偽造や認証の回避が可能になるため、セキュリティ上の重大な問題となります。

このコミットは、このようなタイミング攻撃のリスクを軽減するために、HMAC値の比較に特化した定数時間比較関数 Equal を提供することを目的としています。これにより、開発者が安全な方法でHMACを比較することが容易になり、誤った実装による脆弱性の導入を防ぎます。

前提知識の解説

HMAC (Keyed-Hash Message Authentication Code)

HMACは、メッセージの認証と完全性を保証するためのメカニズムです。共有された秘密鍵とハッシュ関数(例: SHA-256, MD5)を組み合わせてメッセージ認証コード(MAC)を生成します。受信者は同じ鍵とハッシュ関数を使用してMACを再計算し、受信したMACと比較することで、メッセージが改ざんされていないこと、および送信者が秘密鍵を知っていることを確認できます。

HMACの主な目的は以下の通りです。

  • データ完全性: メッセージが転送中に変更されていないことを保証します。
  • 認証: メッセージが正当な送信者から送られたものであることを確認します。

タイミング攻撃 (Timing Side-Channel Attacks)

タイミング攻撃は、暗号学的操作の実行にかかる時間の変動を利用して、秘密情報(例: 秘密鍵、パスワード、MAC値)を推測するサイドチャネル攻撃の一種です。特に、比較操作において、入力が正しいかどうかに応じて処理時間が異なる場合(例: 最初の不一致バイトが見つかった時点で比較を停止するような実装)、攻撃者はその時間の差を観測することで、秘密情報の部分的な情報を得ることができます。

例えば、MACの比較において、正しいMACと攻撃者が推測したMACを比較する際に、最初のバイトが一致すれば比較時間がわずかに長くなり、一致しなければ短くなる、といった挙動があるとします。攻撃者はこれを繰り返し試行することで、MACの各バイトを総当たりで特定し、最終的にMAC全体を解読することが可能になります。

定数時間比較 (Constant-Time Comparison)

定数時間比較とは、比較される入力データの内容に関わらず、常に同じ時間で処理が完了するように設計された比較アルゴリズムです。これにより、タイミング攻撃を防ぐことができます。

定数時間比較の実装では、たとえ途中で不一致が見つかったとしても、比較対象のすべてのバイトを最後まで処理し、その結果を単一のブール値として返します。Go言語の crypto/subtle パッケージに含まれる ConstantTimeCompare 関数は、このような定数時間比較を提供します。

Go言語の crypto/subtle パッケージ

crypto/subtle パッケージは、暗号学的コードでよく必要とされる、タイミング攻撃に対して安全な(定数時間で実行される)低レベルの操作を提供します。このパッケージの関数は、入力値に依存しない実行時間を持つように慎重に実装されており、サイドチャネル攻撃のリスクを軽減します。

このコミットでは、crypto/subtle.ConstantTimeCompare 関数が hmac.Equal 関数の実装に利用されており、HMAC値の比較が安全に行われるようにしています。

技術的詳細

このコミットの主要な変更点は、crypto/hmac パッケージに Equal 関数が追加されたことです。

// Equal compares two MACs for equality without leaking timing information.
func Equal(mac1, mac2 []byte) bool {
	// We don't have to be constant time if the lengths of the MACs are
	// different as that suggests that a completely different hash function
	// was used.
	return len(mac1) == len(mac2) && subtle.ConstantTimeCompare(mac1, mac2) == 1
}

この Equal 関数は、2つのMAC(mac1mac2)を比較します。

  1. まず、2つのMACの長さが異なる場合は、即座に false を返します。これは、異なるハッシュ関数が使用されたことを示唆するため、定数時間である必要がないというコメントがあります。
  2. 長さが同じ場合、subtle.ConstantTimeCompare(mac1, mac2) を呼び出します。この関数は、タイミング攻撃を防ぐために定数時間でバイトスライスを比較し、完全に一致する場合は 1 を、それ以外の場合は 0 を返します。
  3. Equal 関数は、ConstantTimeCompare の結果が 1 であるかどうかをチェックし、その結果を返します。

この実装により、MACの比較処理が、比較される値の内容に依存しない一定の時間で実行されることが保証されます。これにより、攻撃者が比較時間から秘密情報を推測するタイミング攻撃のリスクが排除されます。

また、hmac.go のパッケージコメントも更新され、Equal 関数を使用してMACを比較する例が追加されています。これは、開発者に対して安全な比較方法を明示的に推奨するものです。

/*
Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as
defined in U.S. Federal Information Processing Standards Publication 198.
An HMAC is a cryptographic hash that uses a key to sign a message.
The receiver verifies the hash by recomputing it using the same key.

Receivers should be careful to use Equal to compare MACs in order to avoid
timing side-channels:

	// CheckMAC returns true if messageMAC is a valid HMAC tag for message.
	func CheckMAC(message, messageMAC, key []byte) bool {
		mac := hmac.New(sha256.New, key)
		mac.Write(message)
		expectedMAC := mac.Sum(nil)
		return hmac.Equal(messageMAC, expectedMAC)
	}
*/

さらに、hmac_test.go には TestEqual 関数が追加され、Equal 関数の動作が正しく、かつ安全であることを検証するテストケースが含まれています。

deps_test.go の変更は、crypto/hmaccrypto/subtle に依存するようになったことを反映しています。これは、Goモジュールの依存関係管理の一部であり、crypto/subtle が暗号関連のサポートライブラリとして適切に分類されるように更新されています。

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

src/pkg/crypto/hmac/hmac.go

  • Equal 関数の追加。
  • パッケージコメントの更新と、Equal 関数を使用したMAC比較の推奨例の追加。
  • Reset メソッド内のスライス表記 h.tmp[0:h.blocksize]h.tmp[:h.blocksize] に変更(機能的な変更ではないが、より簡潔な表記)。
  • crypto/subtle パッケージのインポート追加。

src/pkg/crypto/hmac/hmac_test.go

  • TestEqual 関数の追加。Equal 関数の正しい動作と、誤った入力に対する挙動をテスト。

src/pkg/go/build/deps_test.go

  • crypto/hmac の依存関係に CRYPTO-SUPPORT を追加し、crypto/subtle がその一部として定義されるように変更。これにより、ビルドシステムが依存関係を正しく解決できるようになります。

コアとなるコードの解説

hmac.goEqual 関数

func Equal(mac1, mac2 []byte) bool {
	return len(mac1) == len(mac2) && subtle.ConstantTimeCompare(mac1, mac2) == 1
}

この関数は、HMAC値の比較におけるセキュリティの要です。

  1. len(mac1) == len(mac2): まず、比較する2つのMACの長さが等しいかを確認します。長さが異なる場合は、そもそも異なるMACであるため、定数時間比較の必要はなく、即座に false を返します。これは、異なるハッシュ関数が使用された場合など、根本的に異なるMACである可能性が高いからです。
  2. subtle.ConstantTimeCompare(mac1, mac2) == 1: 長さが等しい場合、crypto/subtle パッケージの ConstantTimeCompare 関数を使用して実際のバイト比較を行います。この関数は、入力の内容に関わらず常に同じ時間で比較を実行するため、タイミング攻撃を防ぎます。ConstantTimeCompare は、バイトスライスが完全に一致する場合に 1 を、それ以外の場合に 0 を返します。

この組み合わせにより、MACの比較が安全かつ効率的に行われます。

hmac.go のパッケージコメントの変更

Receivers should be careful to use Equal to compare MACs in order to avoid
timing side-channels:

	// CheckMAC returns true if messageMAC is a valid HMAC tag for message.
	func CheckMAC(message, messageMAC, key []byte) bool {
		mac := hmac.New(sha256.New, key)
		mac.Write(message)
		expectedMAC := mac.Sum(nil)
		return hmac.Equal(messageMAC, expectedMAC)
	}

このコメントは、Equal 関数の導入の意図を明確にし、開発者に対してHMACの検証時にタイミングサイドチャネル攻撃を避けるために Equal 関数を使用するよう強く推奨しています。具体的な使用例が示されているため、開発者はこのパターンに従うことで、安全なHMAC検証を容易に実装できます。

hmac_test.goTestEqual 関数

func TestEqual(t *testing.T) {
	a := []byte("test")
	b := []byte("test1")
	c := []byte("test2")

	if !Equal(b, b) {
		t.Error("Equal failed with equal arguments")
	}
	if Equal(a, b) {
		t.Error("Equal accepted a prefix of the second argument")
	}
	if Equal(b, a) {
		t.Error("Equal accepted a prefix of the first argument")
	}
	if Equal(b, c) {
		t.Error("Equal accepted unequal slices")
	}
}

このテストは、Equal 関数の基本的な機能と、特にタイミング攻撃に関連する挙動を検証します。

  • !Equal(b, b): 同じバイトスライスが与えられた場合に true を返すことを確認します。
  • Equal(a, b)Equal(b, a): 異なる長さのスライスや、一方が他方のプレフィックスである場合に false を返すことを確認します。これは、len(mac1) == len(mac2) のチェックが正しく機能していることを保証します。
  • Equal(b, c): 長さが同じで内容が異なるスライスが与えられた場合に false を返すことを確認します。これは subtle.ConstantTimeCompare が正しく機能していることを保証します。

これらのテストは、Equal 関数が期待通りに動作し、かつ安全な比較を提供していることを確認するための重要なステップです。

関連リンク

参考にした情報源リンク

Please let me know if you have any further questions or need assistance with anything else.