[インデックス 11278] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/hmac パッケージにおける変更を扱っています。具体的には、hmac.NewMD5、hmac.NewSHA1、hmac.NewSHA256 といった特定のハッシュ関数に特化したHMAC生成関数を非推奨とし、より汎用的な hmac.New 関数を使用するように変更を促すものです。この変更は、不要なハッシュ関数のリンクを防ぎ、コードのモジュール性を高めることを目的としています。また、既存のコードベースを新しいAPIに自動的に移行するための gofix ツール用のルールも追加されています。
コミット
commit 8d66a416cb4b84abeaeccaa69dda3783dda1b76a
Author: Luit van Drongelen <luitvd@gmail.com>
Date: Thu Jan 19 17:28:38 2012 -0500
crypto/hmac: Deprecate hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256
Remove NewMD5, NewSHA1 and NewSHA256 in favor of using New and
explicitly importing the used hash-function. This way when using, for
example, HMAC with RIPEMD there's no md5, sha1 and sha256 linked in
through the hmac package.
A gofix rule is included, and applied to the standard library (3 files
altered).
This change is the result of a discussion at
https://golang.org/cl/5550043/ to pull the discussion about
deprecating these functions out of that issue.
R=golang-dev, agl
CC=golang-dev, r, rsc
https://golang.org/cl/5556058
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8d66a416cb4b84abeaeccaa69dda3783dda1b76a
元コミット内容
crypto/hmac パッケージにおいて、hmac.NewMD5、hmac.NewSHA1、hmac.NewSHA256 関数を非推奨とし、削除します。代わりに、より汎用的な hmac.New 関数を使用し、必要なハッシュ関数(例: md5.New、sha1.New、sha256.New)を明示的にインポートして渡す形式に移行します。これにより、hmac パッケージを介して不要なハッシュ関数(例えば、RIPEMDでHMACを使用する場合にMD5、SHA1、SHA256がリンクされること)がリンクされるのを防ぎます。
この変更に伴い、gofix ツールに新しいルールが追加され、標準ライブラリ内の既存のコード(3ファイル)に適用されました。この変更は、https://golang.org/cl/5550043/ で行われた議論から、これらの関数の非推奨化に関する議論を分離して行われたものです。
変更の背景
この変更の主な背景は、Go言語の標準ライブラリにおける依存関係の最適化とモジュール性の向上です。
- 不要な依存関係の排除: 以前の
hmac.NewMD5、hmac.NewSHA1、hmac.NewSHA256のような関数は、crypto/hmacパッケージ自体が特定のハッシュ関数(MD5, SHA1, SHA256)への依存性を持っていました。これは、例えばHMACをRIPEMDなどの他のハッシュ関数と組み合わせて使用する場合でも、MD5やSHA1、SHA256のコードが不必要に最終バイナリにリンクされてしまうことを意味しました。特に組み込みシステムやリソースが限られた環境では、このような不要なコードの増加は問題となります。 - APIの汎用化と柔軟性:
hmac.New関数は、hash.Hashインターフェースを実装する任意のハッシュ関数を受け入れるように設計されています。これにより、ユーザーはHMACで使用するハッシュ関数を自由に選択でき、将来的に新しいハッシュ関数が追加された場合でも、hmacパッケージのAPIを変更することなく対応できるようになります。これは、API設計における「開かれた/閉じた原則 (Open/Closed Principle)」に沿った改善と言えます。 gofixツールの活用: Go言語には、APIの変更に伴う既存コードの自動修正を支援するgofixツールがあります。このコミットでは、非推奨化された関数から新しいAPIへの移行を容易にするために、専用のgofixルールが作成され、標準ライブラリ内の関連ファイルに適用されました。これにより、開発者が手動でコードを修正する手間を省き、スムーズな移行を促進しています。- コミュニティの議論: コミットメッセージに記載されているように、この変更はGoコミュニティ内での議論(
https://golang.org/cl/5550043/)の結果として行われました。これは、Go言語の開発がオープンな議論と合意形成に基づいて進められていることを示しています。
これらの背景から、このコミットはGo言語のライブラリ設計におけるベストプラクティスを追求し、より効率的で柔軟な暗号化機能の提供を目指したものであると理解できます。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念について知っておく必要があります。
-
HMAC (Keyed-Hash Message Authentication Code):
- HMACは、メッセージ認証コード (MAC) の一種で、秘密鍵とハッシュ関数を組み合わせてメッセージの完全性と認証性を保証するメカニズムです。
- メッセージが改ざんされていないこと(完全性)と、メッセージが正当な送信者から送られたこと(認証性)を確認するために使用されます。
- HMACは、任意の暗号学的ハッシュ関数(MD5, SHA-1, SHA-256など)と組み合わせて使用できます。
- Go言語の
crypto/hmacパッケージは、このHMACの機能を提供します。
-
ハッシュ関数 (Cryptographic Hash Function):
- 任意の長さの入力データ(メッセージ)を受け取り、固定長の短い出力(ハッシュ値、メッセージダイジェスト、フィンガープリントなどと呼ばれる)を生成する一方向関数です。
- 暗号学的ハッシュ関数には、以下の特性が求められます。
- 原像計算困難性 (Preimage Resistance): ハッシュ値から元のメッセージを計算するのが計算上困難であること。
- 第二原像計算困難性 (Second Preimage Resistance): あるメッセージと同じハッシュ値を持つ別のメッセージを見つけるのが計算上困難であること。
- 衝突困難性 (Collision Resistance): 異なる2つのメッセージが同じハッシュ値を持つペアを見つけるのが計算上困難であること。
- 代表的なハッシュ関数には、MD5 (Message-Digest Algorithm 5)、SHA-1 (Secure Hash Algorithm 1)、SHA-256 (Secure Hash Algorithm 256) などがあります。Go言語では
crypto/md5、crypto/sha1、crypto/sha256などのパッケージで提供されます。
-
Go言語の
hash.Hashインターフェース:- Go言語の
hashパッケージで定義されているインターフェースで、すべてのハッシュ関数が実装すべきメソッド(Write、Sum、Reset、Size、BlockSize)を定めています。 - これにより、異なるハッシュ関数(MD5, SHA-1, SHA-256など)を統一的な方法で扱うことが可能になります。
hmac.New関数がfunc() hash.Hash型の引数を受け取るのは、このインターフェースのおかげです。
- Go言語の
-
Go言語の
gofixツール:gofixは、Go言語のツールチェーンの一部であり、GoのAPIが変更された際に、古いAPIを使用しているコードを新しいAPIに自動的に書き換えるためのユーティリティです。- Go言語の進化に伴い、APIの改善や変更が行われることがありますが、
gofixを使用することで、開発者は手動での大規模なコード修正を避けることができます。 gofixは、AST (Abstract Syntax Tree) を操作してコードを変換します。
-
Go言語のパッケージとインポート:
- Go言語では、コードはパッケージに分割され、他のパッケージの機能を利用するには
importキーワードを使って明示的にインポートする必要があります。 - このコミットの変更は、
hmacパッケージが特定のハッシュ関数パッケージ(crypto/md5など)を直接インポートするのではなく、ユーザーが必要なハッシュ関数パッケージを明示的にインポートするように促すものです。これにより、最終的なバイナリに含まれるコードを最小限に抑えることができます。
- Go言語では、コードはパッケージに分割され、他のパッケージの機能を利用するには
これらの知識があれば、コミットがなぜ行われたのか、そしてその技術的な影響について深く理解することができます。
技術的詳細
このコミットの技術的詳細は、主に crypto/hmac パッケージのAPI変更と、それに伴う gofix ツールの実装に集約されます。
1. crypto/hmac パッケージのAPI変更
-
変更前:
crypto/hmacパッケージは、特定のハッシュ関数(MD5, SHA1, SHA256)に特化したHMAC生成関数を提供していました。func NewMD5(key []byte) hash.Hash func NewSHA1(key []byte) hash.Hash func NewSHA256(key []byte) hash.Hashこれらの関数は、内部で対応するハッシュ関数パッケージ(
crypto/md5、crypto/sha1、crypto/sha256)をインポートしていました。これにより、たとえユーザーがMD5やSHA1を使用しない場合でも、crypto/hmacをインポートするだけでこれらのハッシュ関数がリンクされてしまう可能性がありました。 -
変更後: 上記の特定のハッシュ関数に特化した関数は削除されました。代わりに、汎用的な
New関数のみが残されました。func New(h func() hash.Hash, key []byte) hash.Hashこの
New関数は、func() hash.Hash型の引数hを受け取ります。これは、hash.Hashインターフェースを実装する新しいハッシュインスタンスを生成する関数を意味します。例えば、MD5を使用したい場合はmd5.Newを、SHA1を使用したい場合はsha1.Newを渡します。例:
- 変更前:
hmac.NewMD5(key) - 変更後:
hmac.New(md5.New, key)
この変更により、
crypto/hmacパッケージ自体は特定のハッシュ関数への直接的な依存性を持たなくなり、ユーザーが必要なハッシュ関数を明示的にインポートする責任を負うことになります。これにより、最終的なバイナリサイズを最適化し、不要なコードのリンクを防ぐことができます。 - 変更前:
2. gofix ツールの実装 (src/cmd/gofix/hmacnew.go)
このコミットの重要な側面は、API変更に伴う既存コードの移行を自動化するために gofix ルールが追加されたことです。
-
hmacnew.goの役割: このファイルは、gofixツールが実行される際に、古いhmac.NewMD5、hmac.NewSHA1、hmac.NewSHA256の呼び出しパターンを検出し、新しいhmac.New形式に自動的に書き換えるためのロジックを実装しています。 -
AST (Abstract Syntax Tree) の操作:
gofixツールは、Goのソースコードをパースして抽象構文木 (AST) を構築し、そのASTを操作することでコードの変換を行います。hmacnew.go内のhmacnew関数は、以下の手順で変換を行います。crypto/hmacのインポートチェック: 変換対象のファイルがcrypto/hmacパッケージをインポートしているかを確認します。インポートしていない場合は、変換の必要がないため処理を終了します。hmac.NewMD5,hmac.NewSHA1,hmac.NewSHA256の検出: ASTを走査し、hmac.NewMD5(...)、hmac.NewSHA1(...)、hmac.NewSHA256(...)のような関数呼び出し (ast.CallExpr) を特定します。- 必要なハッシュ関数のインポート追加: 特定された呼び出しに応じて、対応するハッシュ関数パッケージ(例:
crypto/md5、crypto/sha1、crypto/sha256)のインポートをファイルに追加します。これはaddImportヘルパー関数によって行われます。 - 関数呼び出しの書き換え:
- 関数名 (
ce.Fun) をhmac.Newに変更します。 - 元の引数リストの先頭に、対応するハッシュ関数のコンストラクタ(例:
md5.New、sha1.New、sha256.New)を追加します。 - 例:
hmac.NewMD5(key)はhmac.New(md5.New, key)に変換されます。
- 関数名 (
-
hmacnew_test.go: このファイルには、hmacnewgofixルールの動作を検証するためのテストケースが含まれています。入力コード (In) と期待される出力コード (Out) のペアが定義されており、gofixルールが正しく変換を行うことを保証します。
3. 標準ライブラリへの適用
このコミットでは、gofix ルールが作成されただけでなく、Goの標準ライブラリ内の3つのファイル(src/pkg/crypto/tls/cipher_suites.go、src/pkg/exp/ssh/transport.go、src/pkg/net/smtp/auth.go)に実際に適用されています。これにより、標準ライブラリ自体が新しいAPIに準拠し、他の開発者への模範となります。
これらの技術的詳細から、このコミットが単なるAPIの変更に留まらず、Go言語のエコシステム全体(ライブラリ設計、自動コード変換ツール、標準ライブラリの整合性)を考慮した包括的な改善であることがわかります。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。
-
src/pkg/crypto/hmac/hmac.go:crypto/md5,crypto/sha1,crypto/sha256のインポートが削除されました。NewMD5,NewSHA1,NewSHA256関数が削除されました。New関数のコメントが更新され、crypto.Hashではなくhash.Hash型を使用することが明記されました。
--- a/src/pkg/crypto/hmac/hmac.go +++ b/src/pkg/crypto/hmac/hmac.go @@ -9,9 +9,6 @@ package hmac import ( - "crypto/md5" - "crypto/sha1" - "crypto/sha256" "hash" ) @@ -63,7 +60,7 @@ func (h *hmac) Reset() { h.inner.Write(h.tmp[0:h.blocksize]) } -// New returns a new HMAC hash using the given crypto.Hash type and key. +// New returns a new HMAC hash using the given hash.Hash type and key. func New(h func() hash.Hash, key []byte) hash.Hash { hm := new(hmac) hm.outer = h() @@ -81,12 +78,3 @@ func New(h func() hash.Hash, key []byte) hash.Hash { hm.Reset() return hm } - -// NewMD5 returns a new HMAC-MD5 hash using the given key. -func NewMD5(key []byte) hash.Hash { return New(md5.New, key) } - -// NewSHA1 returns a new HMAC-SHA1 hash using the given key. -func NewSHA1(key []byte) hash.Hash { return New(sha1.New, key) } - -// NewSHA256 returns a new HMAC-SHA256 hash using the given key. -func NewSHA256(key []byte) { return New(sha256.New, key) } -
src/cmd/gofix/hmacnew.go:hmacnewという新しいgofixルールが追加されました。- このルールは、
hmac.NewMD5,hmac.NewSHA1,hmac.NewSHA256の呼び出しを検出し、hmac.Newと対応するハッシュ関数のコンストラクタ(例:md5.New)に書き換えます。 - 同時に、必要なハッシュ関数パッケージのインポートを追加します。
// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import "go/ast" func init() { register(hmacNewFix) } var hmacNewFix = fix{ "hmacnew", "2012-01-19", hmacnew, `Deprecate hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256. This fix rewrites code using hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256 to use hmac.New: hmac.NewMD5(key) -> hmac.New(md5.New, key) hmac.NewSHA1(key) -> hmac.New(sha1.New, key) hmac.NewSHA256(key) -> hmac.New(sha256.New, key) `, } func hmacnew(f *ast.File) (fixed bool) { if !imports(f, "crypto/hmac") { return } walk(f, func(n interface{}) { ce, ok := n.(*ast.CallExpr) if !ok { return } var pkg string switch { case isPkgDot(ce.Fun, "hmac", "NewMD5"): pkg = "md5" case isPkgDot(ce.Fun, "hmac", "NewSHA1"): pkg = "sha1" case isPkgDot(ce.Fun, "hmac", "NewSHA256"): pkg = "sha256" default: return } addImport(f, "crypto/"+pkg) ce.Fun = ast.NewIdent("hmac.New") ce.Args = append([]ast.Expr{ast.NewIdent(pkg + ".New")}, ce.Args...) fixed = true }) return } -
src/cmd/gofix/hmacnew_test.go:hmacnewgofixルールのテストケースが定義されています。これにより、変換ロジックが期待通りに動作することが保証されます。
-
標準ライブラリ内の既存コードの修正:
src/pkg/crypto/tls/cipher_suites.gosrc/pkg/exp/ssh/transport.gosrc/pkg/net/smtp/auth.goこれらのファイルでは、hmac.NewSHA1やhmac.NewMD5の呼び出しが、hmac.New(sha1.New, ...)やhmac.New(md5.New, ...)の形式に修正され、対応するハッシュ関数パッケージがインポートされています。
例 (
src/pkg/crypto/tls/cipher_suites.goの変更):--- a/src/pkg/crypto/tls/cipher_suites.go +++ b/src/pkg/crypto/tls/cipher_suites.go @@ -91,7 +91,7 @@ func macSHA1(version uint16, key []byte) macFunction { copy(mac.key, key) return mac } - return tls10MAC{hmac.NewSHA1(key)} + return tls10MAC{hmac.New(sha1.New, key)} }
これらの変更箇所は、APIの非推奨化、自動移行ツールの実装、そして既存コードへの適用という一連のプロセスを示しています。
コアとなるコードの解説
このコミットのコアとなるコードは、src/cmd/gofix/hmacnew.go に実装された gofix ルールです。このルールは、Goの抽象構文木 (AST) を操作して、古い hmac 関数の呼び出しを新しい形式に変換します。
以下に、hmacnew.go の主要な部分を解説します。
package main
import "go/ast" // GoのASTを扱うためのパッケージ
func init() {
register(hmacNewFix) // gofixツールにこの新しいルールを登録
}
var hmacNewFix = fix{
"hmacnew", // ルールの名前
"2012-01-19", // ルールが導入された日付
hmacnew, // 実際にコード変換を行う関数
`Deprecate hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256.
// ... (変換内容の説明) ...
`,
}
// hmacnew関数は、与えられたASTファイル (f) を走査し、hmac関連の呼び出しを修正する
func hmacnew(f *ast.File) (fixed bool) {
// まず、ファイルが "crypto/hmac" パッケージをインポートしているかを確認
// インポートしていなければ、修正の必要はないので早期リターン
if !imports(f, "crypto/hmac") {
return
}
// ASTを走査するためのヘルパー関数 'walk' を使用
// 各ノード (n) をチェックし、修正が必要な 'ast.CallExpr' (関数呼び出し) を探す
walk(f, func(n interface{}) {
ce, ok := n.(*ast.CallExpr) // ノードが関数呼び出しのASTノードかを確認
if !ok {
return // 関数呼び出しでなければスキップ
}
var pkg string // どのハッシュ関数パッケージ (md5, sha1, sha256) が必要かを示す変数
switch {
// 関数呼び出しが "hmac.NewMD5" の形式かを確認
case isPkgDot(ce.Fun, "hmac", "NewMD5"):
pkg = "md5" // MD5が必要
// 関数呼び出しが "hmac.NewSHA1" の形式かを確認
case isPkgDot(ce.Fun, "hmac", "NewSHA1"):
pkg = "sha1" // SHA1が必要
// 関数呼び出しが "hmac.NewSHA256" の形式かを確認
case isPkgDot(ce.Fun, "hmac", "NewSHA256"):
pkg = "sha256" // SHA256が必要
default:
return // 上記のいずれでもなければスキップ
}
// 必要なハッシュ関数パッケージ (例: "crypto/md5") をファイルにインポート追加
addImport(f, "crypto/"+pkg)
// 関数呼び出しの関数名部分を "hmac.New" に変更
ce.Fun = ast.NewIdent("hmac.New")
// 元の引数リストの先頭に、対応するハッシュ関数のコンストラクタ (例: "md5.New") を追加
// 例: hmac.NewMD5(key) -> hmac.New(md5.New, key)
ce.Args = append([]ast.Expr{ast.NewIdent(pkg + ".New")}, ce.Args...)
fixed = true // 修正が行われたことを示すフラグを立てる
})
return // 修正が行われたかどうかを返す
}
解説のポイント:
go/astパッケージ: Goのソースコードは、go/parserパッケージでパースされるとgo/astパッケージで定義された構造体で表現されるASTになります。gofixツールは、このASTを直接操作することで、コードの構造的な変更を安全に行います。init()関数とregister():init()関数はGoプログラムの起動時に自動的に実行される特殊な関数です。ここでregister(hmacNewFix)を呼び出すことで、このhmacnewルールがgofixツールに認識され、利用可能になります。fix構造体:hmacNewFixはfix構造体のインスタンスで、ルールのメタデータ(名前、日付、説明)と、実際に変換を行う関数 (hmacnew) を保持します。imports(f, "crypto/hmac"): このヘルパー関数は、現在のファイルfが指定されたパッケージ(ここではcrypto/hmac)をインポートしているかどうかをチェックします。これにより、無関係なファイルでの処理をスキップし、効率を高めます。walk(f, func(n interface{}) {...}):walkはASTを深さ優先で走査するための一般的なヘルパー関数です。匿名関数が各ASTノードnに対して実行されます。n.(*ast.CallExpr)と型アサーション:nはinterface{}型なので、(*ast.CallExpr)に型アサーションを行うことで、それが関数呼び出しのASTノードであるかどうかを確認します。isPkgDot(ce.Fun, "hmac", "NewMD5"): このヘルパー関数は、関数呼び出しの対象 (ce.Fun) がhmac.NewMD5のようなパッケージ名.関数名の形式であるかを効率的にチェックします。addImport(f, "crypto/"+pkg): 変換によって新しく必要となるインポート(例:crypto/md5)を、ファイルのインポートリストに自動的に追加します。これは、手動での修正では忘れがちな重要なステップです。- ASTノードの変更:
ce.Fun = ast.NewIdent("hmac.New"): 関数呼び出しの識別子(関数名)をhmac.Newに変更します。ce.Args = append([]ast.Expr{ast.NewIdent(pkg + ".New")}, ce.Args...): これが最も重要な変更です。appendを使って、新しい引数(ハッシュ関数のコンストラクタ、例:md5.New)を既存の引数リストの先頭に追加しています。
この hmacnew.go のコードは、GoのAST操作の典型的な例であり、大規模なコードベースでAPI変更を安全かつ自動的に適用するための強力なメカニズムを示しています。
関連リンク
- Go言語の
crypto/hmacパッケージ: https://pkg.go.dev/crypto/hmac - Go言語の
gofixツールに関するドキュメント (古い情報を含む可能性あり):gofixはGoのバージョンアップに伴い統合されたり、機能が変更されたりしています。一般的な情報はGoの公式ドキュメントやブログ記事を参照してください。- Go 1.0 Release Notes (gofixについて言及): https://go.dev/doc/go1
- Go言語のAST (Abstract Syntax Tree) に関する情報:
go/astパッケージ: https://pkg.go.dev/go/astgo/parserパッケージ: https://pkg.go.dev/go/parser
- Go言語のコードレビューシステム (Gerrit): コミットメッセージに記載されている
golang.org/cl/のリンクは、Goプロジェクトが使用しているGerritベースのコードレビューシステムへのリンクです。- 議論の元となった変更リスト: https://golang.org/cl/5550043/
- このコミットに対応する変更リスト: https://golang.org/cl/5556058
参考にした情報源リンク
- Go言語の公式ドキュメント:
crypto/hmac、hash、go/astなどのパッケージに関する公式ドキュメントは、Go言語の標準ライブラリの動作を理解する上で最も信頼できる情報源です。 - Go言語のソースコード: このコミット自体がGo言語のソースコードの一部であり、変更内容を直接確認することで詳細な理解が得られます。
- Go言語のブログやコミュニティの議論: コミットメッセージに記載されているGerritのリンクは、この変更に至るまでの議論の経緯や背景を理解する上で非常に有用です。
- HMACに関する一般的な情報: WikipediaやRFCなどの標準化文書は、HMACの概念と動作原理を理解するのに役立ちます。
- HMAC - Wikipedia: https://ja.wikipedia.org/wiki/HMAC
- RFC 2104 - HMAC: Keyed-Hashing for Message Authentication: https://datatracker.ietf.org/doc/html/rfc2104
- Go言語の
gofixツールに関する記事:gofixの具体的な使用方法や内部動作について解説している技術ブログ記事なども参考になります。