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

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

このコミットは、Go言語の標準ライブラリ crypto/rand パッケージに Read 関数の使用例を追加するものです。具体的には、rand.Reader から暗号学的に安全な擬似乱数を読み込み、バイトスライスに格納する方法を示すテストコード example_test.go が新規作成されました。これにより、開発者が crypto/rand パッケージの主要な機能である乱数生成をより容易に理解し、適切に利用できるようになります。

コミット

commit 4230dd4c6c4c08f1559d85bc135991627fd89d92
Author: Yves Junqueira <yves.junqueira@gmail.com>
Date:   Wed Aug 8 12:04:54 2012 +1000

    crypto/rand: Example for Read.
    
    R=adg, remyoudompheng, rsc, r
    CC=golang-dev
    https://golang.org/cl/6457085

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

https://github.com/golang/go/commit/4230dd4c6c4c08f1559d85bc135991627fd89d92

元コミット内容

crypto/rand: Example for Read.

R=adg, remyoudompheng, rsc, r
CC=golang-dev
https://golang.org/cl/6457085

変更の背景

Go言語の標準ライブラリは、その堅牢性と使いやすさで知られていますが、各機能の適切な使用方法を示す例は、ライブラリの普及と開発者の生産性向上に不可欠です。crypto/rand パッケージは、セキュリティが要求されるアプリケーションにおいて、予測不可能な乱数を生成するために非常に重要です。しかし、その利用方法が明確でない場合、開発者が誤った方法で乱数を生成し、結果としてセキュリティ上の脆弱性を生み出す可能性があります。

このコミットの背景には、crypto/rand.Read 関数の基本的な使い方を明確に示し、開発者が安全かつ正確に暗号学的に安全な乱数を取得できるようにするという意図があります。Goの_test.goファイルにExample関数を追加する慣習は、ドキュメント生成ツール(go docgodoc.org)によって自動的に実行可能なコード例として抽出され、表示されるため、非常に効果的なドキュメンテーション手法です。これにより、ユーザーはコード例を実際に実行して動作を確認できるため、理解が深まります。

前提知識の解説

1. crypto/rand パッケージ

crypto/rand パッケージは、Go言語において暗号学的に安全な乱数を生成するための機能を提供します。これは、パスワードのソルト、セッションID、鍵生成など、予測不可能性が極めて重要となる場面で利用されます。通常の乱数生成器(例: math/rand)は、シード値が既知であればその出力が予測可能であるため、セキュリティ用途には適していません。crypto/rand は、オペレーティングシステムが提供するエントロピー源(例: /dev/urandom や Windows の CryptGenRandom)を利用して、真に予測不困難な乱数を生成します。

2. rand.Reader

rand.Reader は、crypto/rand パッケージが提供するグローバルな変数で、io.Reader インターフェースを実装しています。これは、暗号学的に安全な乱数のソースとして機能します。Read メソッドを呼び出すことで、指定されたバイトスライスに乱数を書き込むことができます。

3. io.Reader インターフェース

Go言語の io パッケージは、I/Oプリミティブを定義する基本的なインターフェースを提供します。io.Reader インターフェースは、Read(p []byte) (n int, err error) メソッドを一つだけ持ちます。このメソッドは、データを読み込み、読み込んだバイト数とエラーを返します。rand.Reader がこのインターフェースを実装しているため、io パッケージの他の関数(例: io.ReadFull)と組み合わせて使用することができます。

4. io.ReadFull 関数

io.ReadFull(r Reader, buf []byte) (n int, err error) は、io パッケージが提供するヘルパー関数です。これは、指定された io.Reader から、buf スライスの長さと等しいバイト数を読み込むまで繰り返し Read メソッドを呼び出します。途中でエラーが発生した場合や、EOF(End Of File)に達して要求されたバイト数を読み込めなかった場合は、エラーを返します。これにより、確実に指定された量のデータを読み込むことができます。

5. 暗号学的に安全な乱数 (CSPRNG: Cryptographically Secure Pseudorandom Number Generator)

CSPRNGは、その出力が統計的にランダムであるだけでなく、将来の出力を予測することが計算上不可能であるという特性を持つ乱数生成器です。これは、攻撃者が過去の出力や内部状態に関する情報を持っていたとしても、次の出力を推測できないことを意味します。セキュリティ関連のアプリケーションでは、この特性が不可欠です。

技術的詳細

このコミットで追加された ExampleRead 関数は、crypto/rand パッケージの Read メソッドの典型的な使用パターンを示しています。

  1. バイトスライスの準備: 乱数を格納するためのバイトスライス bmake([]byte, c) によって作成されます。ここで c は読み込む乱数のバイト数(この例では10バイト)です。初期状態では、このスライスはゼロで埋められています。
  2. 乱数の読み込み: io.ReadFull(rand.Reader, b) を呼び出すことで、rand.Reader から b の長さ分の乱数が読み込まれます。io.ReadFull を使用することで、b スライスが完全に乱数で埋められることが保証されます。
  3. エラーハンドリング: io.ReadFull は読み込んだバイト数 n とエラー err を返します。n != len(b) または err != nil の場合、乱数の読み込みに失敗したことを意味するため、エラーメッセージを出力して関数を終了します。これは、暗号学的な乱数生成が失敗した場合(例: システムのエントロピー源が枯渇した場合など)に備えた重要なエラーハンドリングです。
  4. 結果の検証: 最後に、bytes.Equal(b, make([]byte, c)) を使用して、読み込んだバイトスライス b がまだゼロで埋められているかどうかをチェックします。暗号学的に安全な乱数が正常に読み込まれていれば、b はゼロ以外のランダムな値で埋められているはずなので、この比較は false を返します。この出力は、Example 関数の特別なコメント // Output: によってテストシステムによって検証されます。

この例は、crypto/rand を使用する際のベストプラクティスを示しています。特に、io.ReadFull を使用して確実に必要なバイト数を読み込むことと、エラーハンドリングの重要性を強調しています。

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

このコミットでは、src/pkg/crypto/rand/example_test.go という新しいファイルが追加されました。

diff --git a/src/pkg/crypto/rand/example_test.go b/src/pkg/crypto/rand/example_test.go
new file mode 100644
index 0000000000..5af8e46f5d
--- /dev/null
+++ b/src/pkg/crypto/rand/example_test.go
@@ -0,0 +1,29 @@
+// 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 rand_test
+
+import (
+	"bytes"
+	"crypto/rand"
+	"fmt"
+	"io"
+)
+
+// This example reads 10 cryptographically secure pseudorandom numbers from
+// rand.Reader and writes them to a byte slice.
+func ExampleRead() {
+	c := 10
+	b := make([]byte, c)
+	n, err := io.ReadFull(rand.Reader, b)
+	if n != len(b) || err != nil {
+		fmt.Println("error:", err)
+		return
+	}
+	// The slice should now contain random bytes instead of only zeroes.
+	fmt.Println(bytes.Equal(b, make([]byte, c)))
+
+	// Output:
+	// false
+}

コアとなるコードの解説

追加された example_test.go ファイル内の ExampleRead 関数は以下の通りです。

package rand_test

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"io"
)

// This example reads 10 cryptographically secure pseudorandom numbers from
// rand.Reader and writes them to a byte slice.
func ExampleRead() {
	c := 10 // 読み込むバイト数を定義
	b := make([]byte, c) // 10バイトのバイトスライスを生成(初期値はすべて0)

	// rand.Reader から b の長さ分の乱数を読み込む
	// io.ReadFull は、b が完全に埋まるまで読み込みを試みる
	n, err := io.ReadFull(rand.Reader, b)

	// 読み込みが成功したか(読み込んだバイト数が期待通りか、エラーがないか)を確認
	if n != len(b) || err != nil {
		fmt.Println("error:", err) // エラーがあれば出力
		return
	}

	// 読み込んだバイトスライス b が、まだゼロで埋められているかを確認
	// 暗号学的に安全な乱数が生成されていれば、b はランダムな値で埋められているはずなので、
	// この比較は false を返すはず
	fmt.Println(bytes.Equal(b, make([]byte, c)))

	// Output:
	// false
	// このコメントは、go test -run ExampleRead を実行した際に、
	// 上記の fmt.Println の出力が "false" であることを期待することを示す
}

このコードは、crypto/rand パッケージの rand.Reader を使って暗号学的に安全な乱数を生成する最も基本的な方法を示しています。

  • package rand_test: このファイルが crypto/rand パッケージのテスト(および例)であることを示します。_test サフィックスは、このパッケージがテスト専用であり、メインのパッケージとは別の名前空間でコンパパイルされることを意味します。
  • import (...): 必要なパッケージをインポートします。
    • bytes: バイトスライスを比較するための bytes.Equal を使用します。
    • crypto/rand: 暗号学的に安全な乱数生成器を提供します。
    • fmt: 出力(エラーメッセージや最終結果)のために使用します。
    • io: io.ReadFull 関数を使用します。
  • func ExampleRead(): Goのテストフレームワークにおける特別な関数名です。Example で始まる関数は、go doc コマンドや godoc.org でドキュメントとして表示され、go test コマンドで実行可能なテストとして扱われます。// Output: コメントと組み合わせることで、関数の出力が期待通りであることを検証できます。
  • c := 10: 読み込む乱数のバイト数を10に設定します。
  • b := make([]byte, c): 10バイトの長さを持つバイトスライス b を作成します。Goでは、make で作成されたスライスは要素型のゼロ値で初期化されるため、この時点ではすべての要素が0です。
  • n, err := io.ReadFull(rand.Reader, b): rand.Reader から b に乱数を読み込みます。io.ReadFull は、b が完全に埋まるまで読み込みを保証します。
  • if n != len(b) || err != nil: 読み込みが成功したかを確認します。n は実際に読み込まれたバイト数、len(b) は期待されるバイト数です。これらが一致しない、またはエラーが発生した場合は、問題があったことを示します。
  • fmt.Println(bytes.Equal(b, make([]byte, c))): 読み込み後の b が、初期状態(すべてゼロ)のスライスと等しいかどうかをチェックします。暗号学的に安全な乱数が正常に生成されていれば、b はランダムな値で埋められているため、この比較は false を返します。
  • // Output: // false: これはGoのテストシステムが認識する特別なコメントで、ExampleRead 関数が実行されたときに標準出力に "false" が出力されることを期待していることを示します。これにより、例が正しく動作していることを自動的に検証できます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (pkg.go.dev)
  • Go言語のソースコード (github.com/golang/go)
  • Go言語のテストとExampleに関する一般的な知識
  • 暗号学的に安全な乱数生成器 (CSPRNG) に関する一般的な情報
  • Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/6457085 (コミットメッセージに記載)# [インデックス 13601] ファイルの概要

このコミットは、Go言語の標準ライブラリ crypto/rand パッケージに Read 関数の使用例を追加するものです。具体的には、rand.Reader から暗号学的に安全な擬似乱数を読み込み、バイトスライスに格納する方法を示すテストコード example_test.go が新規作成されました。これにより、開発者が crypto/rand パッケージの主要な機能である乱数生成をより容易に理解し、適切に利用できるようになります。

コミット

commit 4230dd4c6c4c08f1559d85bc135991627fd89d92
Author: Yves Junqueira <yves.junqueira@gmail.com>
Date:   Wed Aug 8 12:04:54 2012 +1000

    crypto/rand: Example for Read.
    
    R=adg, remyoudompheng, rsc, r
    CC=golang-dev
    https://golang.org/cl/6457085

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

https://github.com/golang/go/commit/4230dd4c6c4c08f1559d85bc135991627fd89d92

元コミット内容

crypto/rand: Example for Read.

R=adg, remyoudompheng, rsc, r
CC=golang-dev
https://golang.org/cl/6457085

変更の背景

Go言語の標準ライブラリは、その堅牢性と使いやすさで知られていますが、各機能の適切な使用方法を示す例は、ライブラリの普及と開発者の生産性向上に不可欠です。crypto/rand パッケージは、セキュリティが要求されるアプリケーションにおいて、予測不可能な乱数を生成するために非常に重要です。しかし、その利用方法が明確でない場合、開発者が誤った方法で乱数を生成し、結果としてセキュリティ上の脆弱性を生み出す可能性があります。

このコミットの背景には、crypto/rand.Read 関数の基本的な使い方を明確に示し、開発者が安全かつ正確に暗号学的に安全な乱数を取得できるようにするという意図があります。Goの_test.goファイルにExample関数を追加する慣習は、ドキュメント生成ツール(go docgodoc.org)によって自動的に実行可能なコード例として抽出され、表示されるため、非常に効果的なドキュメンテーション手法です。これにより、ユーザーはコード例を実際に実行して動作を確認できるため、理解が深まります。

前提知識の解説

1. crypto/rand パッケージ

crypto/rand パッケージは、Go言語において暗号学的に安全な乱数を生成するための機能を提供します。これは、パスワードのソルト、セッションID、鍵生成など、予測不可能性が極めて重要となる場面で利用されます。通常の乱数生成器(例: math/rand)は、シード値が既知であればその出力が予測可能であるため、セキュリティ用途には適していません。crypto/rand は、オペレーティングシステムが提供するエントロピー源(例: /dev/urandom や Windows の CryptGenRandom)を利用して、真に予測困難な乱数を生成します。

2. rand.Reader

rand.Reader は、crypto/rand パッケージが提供するグローバルな変数で、io.Reader インターフェースを実装しています。これは、暗号学的に安全な乱数のソースとして機能します。Read メソッドを呼び出すことで、指定されたバイトスライスに乱数を書き込むことができます。

3. io.Reader インターフェース

Go言語の io パッケージは、I/Oプリミティブを定義する基本的なインターフェースを提供します。io.Reader インターフェースは、Read(p []byte) (n int, err error) メソッドを一つだけ持ちます。このメソッドは、データを読み込み、読み込んだバイト数とエラーを返します。rand.Reader がこのインターフェースを実装しているため、io パッケージの他の関数(例: io.ReadFull)と組み合わせて使用することができます。

4. io.ReadFull 関数

io.ReadFull(r Reader, buf []byte) (n int, err error) は、io パッケージが提供するヘルパー関数です。これは、指定された io.Reader から、buf スライスの長さと等しいバイト数を読み込むまで繰り返し Read メソッドを呼び出します。途中でエラーが発生した場合や、EOF(End Of File)に達して要求されたバイト数を読み込めなかった場合は、エラーを返します。これにより、確実に指定された量のデータを読み込むことができます。

5. 暗号学的に安全な乱数 (CSPRNG: Cryptographically Secure Pseudorandom Number Generator)

CSPRNGは、その出力が統計的にランダムであるだけでなく、将来の出力を予測することが計算上不可能であるという特性を持つ乱数生成器です。これは、攻撃者が過去の出力や内部状態に関する情報を持っていたとしても、次の出力を推測できないことを意味します。セキュリティ関連のアプリケーションでは、この特性が不可欠です。

技術的詳細

このコミットで追加された ExampleRead 関数は、crypto/rand パッケージの Read メソッドの典型的な使用パターンを示しています。

  1. バイトスライスの準備: 乱数を格納するためのバイトスライス bmake([]byte, c) によって作成されます。ここで c は読み込む乱数のバイト数(この例では10バイト)です。初期状態では、このスライスはゼロで埋められています。
  2. 乱数の読み込み: io.ReadFull(rand.Reader, b) を呼び出すことで、rand.Reader から b の長さ分の乱数が読み込まれます。io.ReadFull を使用することで、b スライスが完全に乱数で埋められることが保証されます。
  3. エラーハンドリング: io.ReadFull は読み込んだバイト数 n とエラー err を返します。n != len(b) または err != nil の場合、乱数の読み込みに失敗したことを意味するため、エラーメッセージを出力して関数を終了します。これは、暗号学的な乱数生成が失敗した場合(例: システムのエントロピー源が枯渇した場合など)に備えた重要なエラーハンドリングです。
  4. 結果の検証: 最後に、bytes.Equal(b, make([]byte, c)) を使用して、読み込んだバイトスライス b がまだゼロで埋められているかどうかをチェックします。暗号学的に安全な乱数が正常に読み込まれていれば、b はゼロ以外のランダムな値で埋められているはずなので、この比較は false を返します。この出力は、Example 関数の特別なコメント // Output: によってテストシステムによって検証されます。

この例は、crypto/rand を使用する際のベストプラクティスを示しています。特に、io.ReadFull を使用して確実に必要なバイト数を読み込むことと、エラーハンドリングの重要性を強調しています。

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

このコミットでは、src/pkg/crypto/rand/example_test.go という新しいファイルが追加されました。

diff --git a/src/pkg/crypto/rand/example_test.go b/src/pkg/crypto/rand/example_test.go
new file mode 100644
index 0000000000..5af8e46f5d
--- /dev/null
+++ b/src/pkg/crypto/rand/example_test.go
@@ -0,0 +1,29 @@
+// 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 rand_test
+
+import (
+	"bytes"
+	"crypto/rand"
+	"fmt"
+	"io"
+)
+
+// This example reads 10 cryptographically secure pseudorandom numbers from
+// rand.Reader and writes them to a byte slice.
+func ExampleRead() {
+	c := 10
+	b := make([]byte, c)
+	n, err := io.ReadFull(rand.Reader, b)
+	if n != len(b) || err != nil {
+		fmt.Println("error:", err)
+		return
+	}
+	// The slice should now contain random bytes instead of only zeroes.
+	fmt.Println(bytes.Equal(b, make([]byte, c)))
+
+	// Output:
+	// false
+}

コアとなるコードの解説

追加された example_test.go ファイル内の ExampleRead 関数は以下の通りです。

package rand_test

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"io"
)

// This example reads 10 cryptographically secure pseudorandom numbers from
// rand.Reader and writes them to a byte slice.
func ExampleRead() {
	c := 10 // 読み込むバイト数を定義
	b := make([]byte, c) // 10バイトのバイトスライスを生成(初期値はすべて0)

	// rand.Reader から b の長さ分の乱数を読み込む
	// io.ReadFull は、b が完全に埋まるまで読み込みを試みる
	n, err := io.ReadFull(rand.Reader, b)

	// 読み込みが成功したか(読み込んだバイト数が期待通りか、エラーがないか)を確認
	if n != len(b) || err != nil {
		fmt.Println("error:", err) // エラーがあれば出力
		return
	}

	// 読み込んだバイトスライス b が、まだゼロで埋められているかを確認
	// 暗号学的に安全な乱数が生成されていれば、b はランダムな値で埋められているはずなので、
	// この比較は false を返すはず
	fmt.Println(bytes.Equal(b, make([]byte, c)))

	// Output:
	// false
	// このコメントは、go test -run ExampleRead を実行した際に、
	// 上記の fmt.Println の出力が "false" であることを期待することを示す
}

このコードは、crypto/rand パッケージの rand.Reader を使って暗号学的に安全な乱数を生成する最も基本的な方法を示しています。

  • package rand_test: このファイルが crypto/rand パッケージのテスト(および例)であることを示します。_test サフィックスは、このパッケージがテスト専用であり、メインのパッケージとは別の名前空間でコンパパイルされることを意味します。
  • import (...): 必要なパッケージをインポートします。
    • bytes: バイトスライスを比較するための bytes.Equal を使用します。
    • crypto/rand: 暗号学的に安全な乱数生成器を提供します。
    • fmt: 出力(エラーメッセージや最終結果)のために使用します。
    • io: io.ReadFull 関数を使用します。
  • func ExampleRead(): Goのテストフレームワークにおける特別な関数名です。Example で始まる関数は、go doc コマンドや godoc.org でドキュメントとして表示され、go test コマンドで実行可能なテストとして扱われます。// Output: コメントと組み合わせることで、関数の出力が期待通りであることを検証できます。
  • c := 10: 読み込む乱数のバイト数を10に設定します。
  • b := make([]byte, c): 10バイトの長さを持つバイトスライス b を作成します。Goでは、make で作成されたスライスは要素型のゼロ値で初期化されるため、この時点ではすべての要素が0です。
  • n, err := io.ReadFull(rand.Reader, b): rand.Reader から b に乱数を読み込みます。io.ReadFull は、b が完全に埋まるまで読み込みを保証します。
  • if n != len(b) || err != nil: 読み込みが成功したかを確認します。n は実際に読み込まれたバイト数、len(b) は期待されるバイト数です。これらが一致しない、またはエラーが発生した場合は、問題があったことを示します。
  • fmt.Println(bytes.Equal(b, make([]byte, c))): 読み込み後の b が、初期状態(すべてゼロ)のスライスと等しいかどうかをチェックします。暗号学的に安全な乱数が正常に生成されていれば、b はランダムな値で埋められているため、この比較は false を返します。
  • // Output: // false: これはGoのテストシステムが認識する特別なコメントで、ExampleRead 関数が実行されたときに標準出力に "false" が出力されることを期待していることを示します。これにより、例が正しく動作していることを自動的に検証できます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (pkg.go.dev)
  • Go言語のソースコード (github.com/golang/go)
  • Go言語のテストとExampleに関する一般的な知識
  • 暗号学的に安全な乱数生成器 (CSPRNG) に関する一般的な情報
  • Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/6457085 (コミットメッセージに記載)