[インデックス 13019] ファイルの概要
このコミットは、Go言語の標準ライブラリであるcrypto/x509パッケージにおける、利用不可能なハッシュ関数を使用した場合のパニック(プログラムの異常終了)を修正するものです。具体的には、crypto.Hash.New()の挙動変更に対応し、ハッシュ関数が利用可能かどうかを事前にチェックすることで、安全性を向上させています。
コミット
c8e1946f33ee2cf482922ba2398086189faf53f6
Author: Adam Langley agl@golang.org
Date: Thu May 3 16:39:57 2012 -0400
crypto/x509: fix panic when using unavailable hash function.
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c8e1946f33ee2cf482922ba2398086189faf53f6
元コミット内容
crypto/x509: fix panic when using unavailable hash function.
crypto.Hash.New() changed to panicking when the hash function isn't
linked in, but crypto/x509 still expects it to return nil.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6175047
変更の背景
この変更の背景には、Go言語の暗号化ライブラリにおける内部的な挙動変更があります。以前のcrypto.Hash.New()関数は、指定されたハッシュ関数が利用できない(例えば、コンパイル時にリンクされていない)場合にnilを返していました。しかし、このコミットが行われる少し前に、crypto.Hash.New()の挙動が変更され、利用不可能なハッシュ関数が指定された場合にはnilを返す代わりにパニック(実行時エラーによるプログラムの強制終了)を引き起こすようになりました。
crypto/x509パッケージ内のコードは、このcrypto.Hash.New()の新しい挙動に対応しておらず、引き続きnilが返されることを期待していました。そのため、利用不可能なハッシュ関数がCertificate.CheckSignatureメソッドに渡された際に、crypto.Hash.New()がパニックを引き起こし、プログラムがクラッシュするという問題が発生していました。このコミットは、この互換性の問題を解決し、crypto/x509が新しいcrypto.Hash.New()の挙動に適切に対応できるようにするためのものです。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と暗号技術に関する基本的な知識が必要です。
- Go言語の
panic: Go言語におけるpanicは、回復不可能なエラーが発生した際にプログラムの実行を停止させるメカニズムです。通常、プログラムの継続が不可能または意味をなさない場合に発生し、スタックトレースを出力してプログラムを終了させます。これは、他の言語における「例外」に似ていますが、Goではエラーハンドリングにerrorインターフェースを使用することが推奨されており、panicはより深刻な状況で用いられます。 cryptoパッケージ: Go言語の標準ライブラリには、暗号化機能を提供するcryptoパッケージ群があります。これには、ハッシュ関数(crypto/sha256,crypto/md5など)、暗号化アルゴリズム、デジタル署名などが含まれます。crypto/x509パッケージ:crypto/x509パッケージは、X.509証明書とPKIX(Public Key Infrastructure X.509)標準を扱うための機能を提供します。X.509証明書は、公開鍵暗号システムにおいて、公開鍵の所有者を検証するために広く使用されています。ウェブサイトのHTTPS通信などで利用されるSSL/TLS証明書もX.509証明書の一種です。- ハッシュ関数 (Hash Function): ハッシュ関数は、任意の長さの入力データ(メッセージ)を受け取り、固定長の短いデータ(ハッシュ値、メッセージダイジェスト、フィンガープリントなどと呼ばれる)を出力する一方向性の関数です。暗号学的ハッシュ関数は、入力が少しでも変わると出力が大きく変わる(雪崩効果)、同じハッシュ値を持つ異なる入力を見つけることが困難である(衝突耐性)などの特性を持ち、データの完全性検証やデジタル署名に利用されます。
- デジタル署名 (Digital Signature): デジタル署名は、電子文書の作成者の身元を証明し、文書が改ざんされていないことを保証する技術です。送信者は文書のハッシュ値を計算し、自身の秘密鍵で暗号化して署名を作成します。受信者は送信者の公開鍵で署名を復号し、文書のハッシュ値を再計算して比較することで、署名の正当性と文書の完全性を検証します。
crypto.Hash型:crypto.Hash型は、Go言語のcryptoパッケージで定義されている列挙型(uint8のエイリアス)で、特定のハッシュアルゴリズム(例:crypto.SHA256,crypto.MD5)を表します。この型には、そのハッシュアルゴリズムが利用可能かどうかをチェックするAvailable()メソッドや、新しいハッシュ関数インスタンスを生成するNew()メソッドなどが定義されています。SignatureAlgorithm型:crypto/x509パッケージで定義されている列挙型で、デジタル署名に使用されるアルゴリズム(例:SHA256WithRSA,ECDSAWithSHA384など)を表します。
技術的詳細
このコミットが修正している問題は、crypto.Hash.New()関数のセマンティクス(挙動)の変更と、それに対応していなかったcrypto/x509パッケージのコードとの間の不整合に起因します。
-
旧来の
crypto.Hash.New()の挙動: 以前は、crypto.Hash型のNew()メソッドは、対応するハッシュアルゴリズムがシステムにリンクされていない(利用できない)場合、エラーを示すためにnilを返していました。crypto/x509パッケージのCertificate.CheckSignatureメソッドは、このnil戻り値をチェックし、ErrUnsupportedAlgorithmを返すことで、サポートされていないアルゴリズムが使用されたことを適切に処理していました。// 変更前 (概念的なコード) h := hashType.New() // hashTypeが利用不可の場合、hはnilになる if h == nil { return ErrUnsupportedAlgorithm } -
crypto.Hash.New()の新しい挙動: このコミットの少し前に、crypto.Hash.New()の内部実装が変更され、利用不可能なハッシュアルゴリズムに対してはnilを返す代わりにpanicを引き起こすようになりました。これは、通常、利用可能なハッシュ関数のみがNew()に渡されるべきであり、利用不可能なハッシュ関数が渡されることはプログラミングエラーであるという設計思想に基づいている可能性があります。 -
crypto/x509の不整合:crypto/x509パッケージのCertificate.CheckSignatureメソッドは、このcrypto.Hash.New()の挙動変更を認識していませんでした。そのため、hashType.New()を呼び出した後、以前のようにnilチェックを行っていましたが、実際にはpanicが発生するため、このチェックに到達する前にプログラムがクラッシュしていました。 -
修正内容: このコミットでは、
crypto.Hash.New()を呼び出す前に、hashType.Available()メソッドを使ってハッシュアルゴリズムが利用可能かどうかを明示的にチェックするようになりました。// 変更後 if !hashType.Available() { // ハッシュ関数が利用可能か事前にチェック return ErrUnsupportedAlgorithm } h := hashType.New() // 利用可能であることが保証された上でNew()を呼び出すhashType.Available()は、そのハッシュアルゴリズムがGoのランタイムに登録され、利用可能である場合にtrueを返します。このチェックを先に行うことで、利用不可能なハッシュ関数が渡された場合でもpanicを回避し、代わりにErrUnsupportedAlgorithmを返すという、以前の期待されたエラーハンドリングの挙動を維持できるようになりました。これにより、crypto/x509パッケージの堅牢性が向上し、予期せぬクラッシュを防ぐことができます。
コアとなるコードの変更箇所
変更はsrc/pkg/crypto/x509/x509.goファイル内のCertificate構造体のCheckSignatureメソッドにあります。
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -388,10 +388,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return ErrUnsupportedAlgorithm
}
-\th := hashType.New()\n-\tif h == nil {\n+\tif !hashType.Available() {\n return ErrUnsupportedAlgorithm
}\n+\th := hashType.New()\n
h.Write(signed)
digest := h.Sum(nil)
コアとなるコードの解説
変更されたCheckSignatureメソッドは、X.509証明書のデジタル署名を検証する役割を担っています。このメソッドは、署名アルゴリズム(algo)、署名対象のデータ(signed)、および実際の署名データ(signature)を受け取ります。
変更前のコードでは、以下のようになっていました。
h := hashType.New()
if h == nil {
return ErrUnsupportedAlgorithm
}
ここで、hashTypeはSignatureAlgorithmから導出されたcrypto.Hash型の値です。以前は、hashType.New()がnilを返すことで、そのハッシュアルゴリズムが利用できないことを示していました。
変更後のコードは以下のようになっています。
if !hashType.Available() {
return ErrUnsupportedAlgorithm
}
h := hashType.New()
この変更により、hashType.New()を呼び出す前に、hashType.Available()という新しいチェックが追加されました。
hashType.Available(): このメソッドは、対応するハッシュアルゴリズムがGoのランタイムで利用可能である場合にtrueを返します。利用できない場合はfalseを返します。if !hashType.Available(): この条件文は、「もしハッシュアルゴリズムが利用可能でなければ」という意味になります。return ErrUnsupportedAlgorithm: ハッシュアルゴリズムが利用可能でない場合、ErrUnsupportedAlgorithmエラーを返して処理を終了します。これにより、panicを回避し、呼び出し元に適切なエラーを通知できます。h := hashType.New(): この行は、hashType.Available()がtrueを返した場合にのみ実行されます。つまり、ここでNew()が呼び出される際には、ハッシュアルゴリズムが確実に利用可能であることが保証されているため、panicが発生する可能性がなくなります。
この修正は、crypto.Hash.New()の挙動変更にcrypto/x509パッケージが適応するための重要な変更であり、Go言語の標準ライブラリの堅牢性と互換性を維持するために行われました。
関連リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/c8e1946f33ee2cf482922ba2398086189faf53f6
- Go Code Review (CL) ページ: https://golang.org/cl/6175047
参考にした情報源リンク
- Go言語の
cryptoパッケージに関する公式ドキュメント (当時のバージョンに基づく):crypto/x509パッケージ: https://pkg.go.dev/crypto/x509cryptoパッケージ: https://pkg.go.dev/crypto
- Go言語における
panicとrecoverの概念: https://go.dev/blog/defer-panic-and-recover - デジタル署名とハッシュ関数の基本概念 (一般的な情報源):
- Go言語のコミット履歴と変更ログ (当時の情報):
- Goのリリースノートや変更履歴は、特定の時期のAPI変更を追跡するのに役立ちます。