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

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

このコミットは、Go言語の標準ライブラリ crypto/rand パッケージにおける Int および Prime 関数のテストを追加するものです。具体的には、src/pkg/crypto/rand/util_test.go ファイルに新しいテストケースが追加されています。

コミット

commit 99d23dfdfd19204d76877a7322c298726aba6d9a
Author: Shawn Smith <shawn.p.smith@gmail.com>
Date:   Fri Jan 31 11:43:48 2014 +1100

    crypto/rand: add tests for Int, Prime
    
    LGTM=rsc, dave
    R=golang-codereviews, dave, josharian, rsc
    CC=golang-codereviews
    https://golang.org/cl/46490043

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

https://github.com/golang/go/commit/99d23dfdfd19204d76877a7322c298726aba6d9a

元コミット内容

crypto/rand: Int, Prime のテストを追加

変更の背景

このコミットの背景には、crypto/rand パッケージの Int および Prime 関数に対するテストカバレッジを向上させる目的があります。特に、これらの関数が不正な入力(例えば、Prime に2未満のビット数を渡す、Int に0以下の最大値を渡す)を受け取った際の挙動を検証することが重要です。暗号学的に安全な乱数を生成するこれらの関数は、セキュリティ上極めて重要であり、あらゆるエッジケースでの堅牢性を保証するための厳密なテストが不可欠です。

Go言語の標準ライブラリは、その品質と信頼性を維持するために、徹底したテストが求められます。このコミットは、その品質保証プロセスの一環として、特定の関数のコーナーケースにおける挙動を明示的にテストすることで、ライブラリ全体の信頼性を高めることを目的としています。

前提知識の解説

crypto/rand パッケージ

crypto/rand パッケージは、暗号学的に安全な乱数ジェネレータ(CSPRNG: Cryptographically Secure PseudoRandom Number Generator)へのアクセスを提供します。これは、鍵生成、セッションID、ノンス(nonce)など、予測不可能性が不可欠なセキュリティ関連の用途で利用されます。このパッケージの乱数は、オペレーティングシステムが提供する乱数源(例: Linuxの/dev/urandom)から取得されるため、高品質で予測不困難な乱数を提供します。

rand.Int(rand.Reader, max *big.Int) (*big.Int, error)

この関数は、[0, max) の範囲で暗号学的に安全なランダムな整数を生成します。rand.Reader は、乱数源として使用される io.Reader インターフェースを満たすオブジェクトです。max は生成される乱数の上限(排他的)を指定する *big.Int 型のポインタです。max が0以下の場合、この関数はパニック(panic)を引き起こします。

rand.Prime(rand.Reader, bits int) (*big.Int, error)

この関数は、指定されたビット数 bits を持つ暗号学的に安全なランダムな素数を生成します。bits は生成される素数のビット長を指定します。この関数は、主にRSAなどの公開鍵暗号システムにおける鍵生成に使用されます。bits が2未満の場合、この関数はエラーを返します。

math/big パッケージ

Go言語の math/big パッケージは、任意精度の算術演算をサポートします。これは、標準のGoの整数型(int, int64 など)では表現できない非常に大きな整数を扱う必要がある場合に不可欠です。crypto/rand パッケージの Int および Prime 関数は、大きな数値を扱うため、この math/big パッケージの *big.Int 型を使用します。

Go言語のテスト

Go言語では、テストは _test.go で終わるファイルに記述され、testing パッケージを使用します。テスト関数は Test で始まり、*testing.T 型の引数を取ります。t.Errorft.Fatalf などのメソッドを使用して、テストの失敗を報告します。panic のテストには deferrecover を組み合わせて使用することが一般的です。

技術的詳細

このコミットで追加されたテストは、crypto/rand パッケージの Int および Prime 関数の特定の挙動、特にエラーハンドリングとパニックの発生条件を検証することに焦点を当てています。

TestPrimeBitsLt2

このテストは、rand.Prime 関数に2未満のビット数(具体的には1ビット)を渡した場合の挙動を検証します。Prime 関数は、2未満のビット数では有効な素数を生成できないため、nil とエラーを返すことが期待されます。このテストは、その期待されるエラーハンドリングが正しく実装されていることを確認します。

TestInt

このテストは、rand.Int 関数が様々な max 値(128から139まで)に対して正しく動作することを確認します。特に、max.BitLen() % 8 == 0 のケース(つまり、max がちょうどバイト境界に収まる場合)がカバーされるように、128からテストを開始しています。これは、Int 関数の内部実装がバイト単位で処理を行う可能性があるため、境界条件での正確性を保証するために重要です。テストは、エラーが発生しないことを確認するだけで、生成された乱数の値自体は検証していません。これは、乱数の予測不可能性をテストすることが困難であるため、通常は乱数生成器の出力範囲とエラー条件のみをテストします。

testIntPanics

このヘルパー関数は、rand.Int がパニックを引き起こすことを期待するテストケースで使用されます。deferrecover を利用して、関数呼び出しがパニックを引き起こしたかどうかを捕捉し、パニックが発生しなかった場合にテストを失敗させます。これは、Go言語で意図的なパニックの発生をテストするための標準的なパターンです。

TestIntEmptyMaxPanics

このテストは、rand.Int 関数に空の big.Int(値が0)を max として渡した場合にパニックが発生することを確認します。rand.Int のドキュメントでは、max が0以下の場合にパニックすると明記されており、このテストはその仕様が正しく実装されていることを検証します。

TestIntNegativeMaxPanics

このテストは、rand.Int 関数に負の値(-1)を max として渡した場合にパニックが発生することを確認します。これも rand.Int の仕様に基づいたテストであり、不正な入力に対する関数の堅牢性を保証します。

これらのテストは、関数の入力検証とエラー/パニックハンドリングの正確性を保証することで、crypto/rand パッケージの信頼性と安全性を高めることに貢献しています。

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

変更は src/pkg/crypto/rand/util_test.go ファイルに集中しており、以下のコードが追加されています。

--- a/src/pkg/crypto/rand/util_test.go
+++ b/src/pkg/crypto/rand/util_test.go
@@ -6,6 +6,7 @@ package rand_test
 
  import (
  	"crypto/rand"
+ 	"math/big"
  	"testing"
  )
 
@@ -24,3 +25,41 @@ func TestPrimeSmall(t *testing.T) {
  		}
  	}
  }
++
+// Test that passing bits < 2 causes Prime to return nil, error
+func TestPrimeBitsLt2(t *testing.T) {
++	if p, err := rand.Prime(rand.Reader, 1); p != nil || err == nil {
++		t.Errorf("Prime should return nil, error when called with bits < 2")
++	}
++}
++
+func TestInt(t *testing.T) {
++	// start at 128 so the case of (max.BitLen() % 8) == 0 is covered
++	for n := 128; n < 140; n++ {
++		b := new(big.Int).SetInt64(int64(n))
++		if i, err := rand.Int(rand.Reader, b); err != nil {
++			t.Fatalf("Can't generate random value: %v, %v", i, err)
++		}
++	}
++}
++
+func testIntPanics(t *testing.T, b *big.Int) {
++	defer func() {
++		if err := recover(); err == nil {
++			t.Errorf("Int should panic when called with max <= 0: %v", b)
++		}
++	}()
++	rand.Int(rand.Reader, b)
++}
++
+// Test that passing a new big.Int as max causes Int to panic
+func TestIntEmptyMaxPanics(t *testing.T) {
++	b := new(big.Int)
++	testIntPanics(t, b)
++}
++
+// Test that passing a negative value as max causes Int to panic
+func TestIntNegativeMaxPanics(t *testing.T) {
++	b := new(big.Int).SetInt64(int64(-1))
++	testIntPanics(t, b)
++}

コアとなるコードの解説

import "math/big" の追加

rand.Int 関数が *big.Int 型を引数に取るため、そのテストコード内で big.Int オブジェクトを生成するために math/big パッケージのインポートが必要になります。

TestPrimeBitsLt2 関数

func TestPrimeBitsLt2(t *testing.T) {
	if p, err := rand.Prime(rand.Reader, 1); p != nil || err == nil {
		t.Errorf("Prime should return nil, error when called with bits < 2")
	}
}

このテストは、rand.Primebits として 1 を渡しています。Prime 関数は2未満のビット数では素数を生成できないため、戻り値 pnil であり、かつ errnil でない(つまりエラーが発生している)ことを期待します。もし pnil でないか、errnil であれば、テストは失敗します。

TestInt 関数

func TestInt(t *testing.T) {
	// start at 128 so the case of (max.BitLen() % 8) == 0 is covered
	for n := 128; n < 140; n++ {
		b := new(big.Int).SetInt64(int64(n))
		if i, err := rand.Int(rand.Reader, b); err != nil {
			t.Fatalf("Can't generate random value: %v, %v", i, err)
		}
	}
}

このテストは、max の値が128から139までの範囲で rand.Int を呼び出しています。ループの目的は、max のビット長が8の倍数になるケース(n=128 の場合)を含む、様々な max 値で関数がエラーなく動作することを確認することです。rand.Int がエラーを返した場合、t.Fatalf を呼び出してテストを即座に終了させます。

testIntPanics 関数

func testIntPanics(t *testing.T, b *big.Int) {
	defer func() {
		if err := recover(); err == nil {
			t.Errorf("Int should panic when called with max <= 0: %v", b)
		}
	}()
	rand.Int(rand.Reader, b)
}

これはヘルパー関数で、rand.Int がパニックを引き起こすことを期待するテストケースで使用されます。defer ステートメント内の匿名関数は、rand.Int(rand.Reader, b) の呼び出しがパニックした場合に実行されます。recover() はパニックの値を捕捉し、パニックが発生しなかった場合(errnil の場合)に t.Errorf を呼び出してテストを失敗させます。これにより、関数が期待通りにパニックしたことを検証できます。

TestIntEmptyMaxPanics 関数

func TestIntEmptyMaxPanics(t *testing.T) {
	b := new(big.Int) // b is 0
	testIntPanics(t, b)
}

このテストは、max として値が0の big.Intrand.Int に渡した場合に、testIntPanics ヘルパー関数を使ってパニックが発生することを確認します。

TestIntNegativeMaxPanics 関数

func TestIntNegativeMaxPanics(t *testing.T) {
	b := new(big.Int).SetInt64(int64(-1)) // b is -1
	testIntPanics(t, b)
}

このテストは、max として負の値(-1)を持つ big.Intrand.Int に渡した場合に、testIntPanics ヘルパー関数を使ってパニックが発生することを確認します。

これらのテストは、crypto/rand パッケージの Int および Prime 関数が、不正な入力に対して期待されるエラーまたはパニックの挙動を示すことを保証し、ライブラリの堅牢性と信頼性を向上させます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のテストに関する一般的なプラクティス
  • crypto/rand パッケージのソースコード