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

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

このコミットは、Go言語の標準ライブラリ crypto/ocsp パッケージ内のテストコードにおける論理的な誤りを修正するものです。具体的には、OCSP (Online Certificate Status Protocol) レスポンスのデコードテストにおいて、ThisUpdateNextUpdate フィールドの検証が、期待される値 (expected) と比較されるべきところを、誤って自分自身 (resp) と比較していた問題を修正しています。

コミット

commit 6f975fbb31d97606154b8e753389fb1410a91de6
Author: Christopher Wedgwood <cw@f00f.org>
Date:   Tue Dec 13 14:40:28 2011 -0500

    cypto/ocsp: fix tests
    
    Actually compare the result with expected not itself
    
    R=agl, bradfitz
    CC=golang-dev, rsc
    https://golang.org/cl/5477079

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

https://github.com/golang/go/commit/6f975fbb31d97606154b8e753389fb1410a91de6

元コミット内容

cypto/ocsp: fix tests Actually compare the result with expected not itself

このコミットメッセージは、crypto/ocsp パッケージのテストを修正したことを示しています。修正内容は、「結果をそれ自身と比較するのではなく、実際に期待される値と比較する」というものです。これは、テストコードにおける一般的なバグパターン、すなわちアサーションが常に真となってしまうような誤った比較ロジックを指しています。

変更の背景

ソフトウェア開発において、テストはコードの品質と信頼性を保証するために不可欠です。特に暗号化やセキュリティ関連のライブラリでは、正確な動作が極めて重要となります。このコミットは、crypto/ocsp パッケージのテストコードに存在する、OCSPレスポンスのデコード結果を検証する際の論理的な誤りを修正するために行われました。

元のテストコードでは、デコードされたOCSPレスポンスの ThisUpdate および NextUpdate フィールドが、期待される値と比較されるべきであるにもかかわらず、誤ってそれ自身と比較されていました。reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) のような比較は、常に true を返すため、実際のデコード結果が正しくなくてもテストがパスしてしまうという問題がありました。このようなテストは、コードのバグを見つける役割を果たさず、誤った安心感を与えてしまいます。

この修正は、テストの有効性を回復し、OCSPレスポンスのデコード処理が仕様通りに機能していることを確実に検証できるようにすることを目的としています。

前提知識の解説

OCSP (Online Certificate Status Protocol)

OCSPは、X.509デジタル証明書の失効状態をリアルタイムで確認するためのインターネットプロトコルです。従来のCRL (Certificate Revocation List) が証明書失効情報をリストとして配布するのに対し、OCSPは特定の証明書の失効状態をOCSPレスポンダーに問い合わせることで、より迅速かつ効率的に情報を取得できます。

OCSPの基本的な流れは以下の通りです。

  1. クライアントは、検証したい証明書の情報をOCSPリクエストとしてOCSPレスポンダーに送信します。
  2. OCSPレスポンダーは、その証明書の失効状態(有効、失効、不明)をデータベースで確認します。
  3. OCSPレスポンダーは、確認結果をデジタル署名されたOCSPレスポンスとしてクライアントに返します。
  4. クライアントは、OCSPレスポンスの署名を検証し、証明書の失効状態を判断します。

OCSPレスポンスには、証明書のシリアル番号、失効状態、ThisUpdate (このレスポンスが発行された時刻)、NextUpdate (このレスポンスが有効であると期待される次の時刻) などの情報が含まれます。

Go言語の reflect.DeepEqual

reflect.DeepEqual はGo言語の reflect パッケージで提供される関数で、2つの引数が「深く」等しいかどうかを判定します。これは、プリミティブ型だけでなく、構造体、配列、スライス、マップなどの複合型についても、その内容が再帰的に等しいかを比較します。

  • プリミティブ型: 値が等しいか。
  • 構造体: すべてのエクスポートされたフィールドが DeepEqual であるか。
  • 配列: 要素が順に DeepEqual であるか。
  • スライス: 長さが等しく、要素が順に DeepEqual であるか。
  • マップ: キーと値のペアがすべて DeepEqual であるか。
  • ポインタ: 指している値が DeepEqual であるか。

DeepEqual は、テストコードで期待される結果と実際の結果を比較する際によく使用されます。しかし、このコミットで示されているように、誤って同じ変数を2回渡してしまうと、常に true を返してしまうため、テストとしての意味を失います。

Go言語のテストフレームワーク

Go言語には、標準ライブラリとして軽量なテストフレームワークが組み込まれています。

  • テストファイルは _test.go というサフィックスを持つ必要があります。
  • テスト関数は Test で始まり、*testing.T 型の引数を取ります (例: func TestMyFunction(t *testing.T) )。
  • テストの失敗を報告するには、t.Error()t.Errorf() を使用します。これらはテストを失敗としてマークしますが、テスト関数の実行は継続します。
  • t.Fatal()t.Fatalf() は、テストを失敗としてマークし、テスト関数の実行を即座に停止します。

このコミットのコードスニペットでは、t.Errorf() が使用されており、これはテストが失敗した場合にエラーメッセージを出力しつつ、残りのテストロジックの実行を継続することを示しています。

技術的詳細

このコミットの核心は、Go言語の reflect.DeepEqual 関数の誤用と、それによって引き起こされるテストの無効化です。

元のコードでは、OCSPレスポンスのデコード結果である resp 構造体の ThisUpdate および NextUpdate フィールドを検証する際に、以下のような比較が行われていました。

if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) {
    t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate)
}

この if 文の条件 !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) は、常に !true、つまり false と評価されます。なぜなら、どのような値であってもそれ自身とは常に DeepEqual であるためです。結果として、t.Errorf が呼び出されることはなく、resp.ThisUpdate の値が expected.ThisUpdate と異なっていてもテストはパスしてしまいます。これは、テストが本来検出するべきバグを見逃す「偽陽性」の状態を作り出します。

修正後のコードでは、この比較が以下のように変更されました。

if !reflect.DeepEqual(resp.ThisUpdate, expected.ThisUpdate) {
    t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate)
}

これにより、resp.ThisUpdate (実際のデコード結果) と expected.ThisUpdate (テストで期待される正しい値) が比較されるようになります。もし両者が等しくなければ reflect.DeepEqualfalse を返し、! 演算子によって条件が true となり、t.Errorf が呼び出されてテストが失敗します。これにより、テストがOCSPレスポンスのデコード処理の正確性を正しく検証できるようになります。

同様の修正が NextUpdate フィールドにも適用されています。

この修正は、単なるタイポやコピー&ペーストのミスに起因するものであり、テストコードの品質と信頼性を維持する上で非常に重要です。

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

変更は src/pkg/crypto/ocsp/ocsp_test.go ファイルの以下の部分です。

--- a/src/pkg/crypto/ocsp/ocsp_test.go
+++ b/src/pkg/crypto/ocsp/ocsp_test.go
@@ -23,11 +23,11 @@ func TestOCSPDecode(t *testing.T) {
 		NextUpdate:       time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC),\n     	}\n     \n-	if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) {\n+	if !reflect.DeepEqual(resp.ThisUpdate, expected.ThisUpdate) {\n \t\tt.Errorf(\"resp.ThisUpdate: got %d, want %d\", resp.ThisUpdate, expected.ThisUpdate)\n \t}\n \n-	if !reflect.DeepEqual(resp.NextUpdate, resp.NextUpdate) {\n+	if !reflect.DeepEqual(resp.NextUpdate, expected.NextUpdate) {\n \t\tt.Errorf(\"resp.NextUpdate: got %d, want %d\", resp.NextUpdate, expected.NextUpdate)\n \t}\n \n```

## コアとなるコードの解説

`TestOCSPDecode` 関数は、OCSPレスポンスのデコード処理をテストするためのものです。このテストでは、事前に定義された期待されるOCSPレスポンスの構造体 `expected` と、実際にデコードされたOCSPレスポンスの構造体 `resp` を比較します。

変更前のコードでは、`resp.ThisUpdate` と `resp.NextUpdate` の検証において、`reflect.DeepEqual` の第二引数に誤って `resp.ThisUpdate` および `resp.NextUpdate` 自身が渡されていました。

-   `- if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) {`
-   `- if !reflect.DeepEqual(resp.NextUpdate, resp.NextUpdate) {`

これは、常に `true` と評価される条件式となり、`t.Errorf` が実行されることはありませんでした。つまり、デコード結果が間違っていてもテストは成功してしまっていたのです。

このコミットによって、`reflect.DeepEqual` の第二引数が `expected` 構造体の対応するフィールドに修正されました。

-   `+ if !reflect.DeepEqual(resp.ThisUpdate, expected.ThisUpdate) {`
-   `+ if !reflect.DeepEqual(resp.NextUpdate, expected.NextUpdate) {`

これにより、`resp` の実際の値と `expected` の期待値が正しく比較されるようになり、デコード処理に問題があればテストが適切に失敗するようになりました。これは、テストの信頼性と有効性を大幅に向上させる重要な修正です。

## 関連リンク

-   Go言語の `reflect` パッケージドキュメント: [https://pkg.go.dev/reflect](https://pkg.go.dev/reflect)
-   Go言語の `testing` パッケージドキュメント: [https://pkg.go.dev/testing](https://pkg.go.dev/testing)
-   RFC 6960 - X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP: [https://datatracker.ietf.org/doc/html/rfc6960](https://datatracker.ietf.org/doc/html/rfc6960)

## 参考にした情報源リンク

-   Go言語の公式ドキュメント
-   RFC 6960 (OCSPの仕様)
-   一般的なソフトウェアテストの原則とベストプラクティスに関する知識