[インデックス 13297] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/rand
パッケージがPlan 9オペレーティングシステム上で動作するように修正するものです。具体的には、暗号論的に安全な乱数ジェネレータである rand.Reader
がPlan 9環境で適切に初期化され、利用できるようになります。
コミット
commit 3476c2312492947e1c300921c5d4f1bce0e07ef5
Author: Markus Sonderegger <marraison@gmail.com>
Date: Wed Jun 6 16:05:47 2012 -0400
crypto/rand: enable rand.Reader on plan9
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6297044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3476c2312492947e1c300921c5d4f1bce0e07ef5
元コミット内容
crypto/rand: enable rand.Reader on plan9
変更の背景
Go言語の crypto/rand
パッケージは、暗号論的に安全な乱数を生成するための機能を提供します。Unix系のシステム(Linux, macOS, FreeBSDなど)では、通常 /dev/urandom
や /dev/random
といった特殊なデバイスファイルからシステムエントロピー(乱雑さの源)を読み取ることで乱数を生成します。
しかし、Plan 9オペレーティングシステムは、Unixとは異なる設計思想を持つため、/dev/urandom
のような標準的なデバイスファイルが存在しません。このコミット以前は、crypto/rand
パッケージのUnix固有の実装 (rand_unix.go
) はPlan 9向けにビルドされず、結果としてPlan 9環境では rand.Reader
が利用できませんでした。
この変更の背景には、Go言語がより多くのプラットフォームをサポートし、特にPlan 9のようなニッチな、しかし重要なシステムでも暗号機能を活用できるようにするという意図があります。Plan 9は、ベル研究所で開発された分散オペレーティングシステムであり、その「すべてがファイルである」という哲学は、Go言語の設計思想とも一部共通する点があります。このコミットは、Plan 9上でのGoアプリケーションがセキュアな乱数を必要とする場合に、その基盤を提供することを目的としています。
前提知識の解説
1. crypto/rand
パッケージと rand.Reader
Go言語の crypto/rand
パッケージは、暗号論的に安全な乱数ジェネレータ (CSPRNG) を提供します。これは、予測不可能な乱数を生成するために使用され、セキュリティが重要なアプリケーション(鍵生成、セッションID生成など)で不可欠です。
rand.Reader
は、io.Reader
インターフェースを満たすグローバルな変数であり、このパッケージの主要なエントリポイントです。アプリケーションは rand.Reader.Read(b []byte)
を呼び出すことで、暗号論的に安全な乱数バイトを b
に読み込むことができます。
2. /dev/urandom
/dev/urandom
は、LinuxやmacOSなどのUnix系オペレーティングシステムに存在する特殊なデバイスファイルです。これは、システムが収集したエントロピー(マウスの動き、キーボード入力、ディスクI/O、ネットワークアクティビティなど)を基に、暗号論的に安全な擬似乱数を生成して提供します。/dev/urandom
はブロックせず、常に乱数を供給し続けるため、多くのアプリケーションで利用されます。
3. Plan 9 オペレーティングシステム
Plan 9 from Bell Labsは、Unixの設計者の一部によって開発された分散オペレーティングシステムです。その最も特徴的な設計原則は「すべてがファイルである」というもので、システム内のあらゆるリソース(プロセス、ネットワーク接続、デバイスなど)がファイルシステムを通じてアクセスされます。この哲学は、Unixの「すべてがファイルである」という考え方をさらに推し進めたものです。
Plan 9は、Unixとは異なるカーネルアーキテクチャとシステムコールインターフェースを持ち、デバイスファイルの命名規則やアクセス方法も異なります。そのため、Unixで一般的な /dev/urandom
のようなパスはPlan 9には存在しません。
4. Goのビルドタグ (+build
)
Go言語では、ソースファイルの先頭に +build
ディレクティブを記述することで、特定の環境でのみそのファイルをコンパイルするように制御できます。例えば、// +build linux
と書かれたファイルはLinux環境でのみコンパイルされます。この機能は、OS固有のコードやアーキテクチャ固有のコードを分離するために使用されます。
5. bufio.Reader
bufio.Reader
は、Go言語の bufio
パッケージが提供するバッファリングされたリーダーです。これは、基になる io.Reader
からデータを読み取る際に、内部バッファを使用してI/O操作の回数を減らし、パフォーマンスを向上させます。特に、小さなチャンクで頻繁に読み取りを行う場合に効果的です。
技術的詳細
このコミットは、src/pkg/crypto/rand/rand_unix.go
ファイルに対して行われています。このファイルは、Unix系のシステムにおける crypto/rand
の実装を提供しています。
主な変更点は以下の通りです。
-
ビルドタグへの
plan9
の追加: ファイルの先頭にあるビルドタグ// +build darwin freebsd linux netbsd openbsd
にplan9
が追加されました。これにより、このファイルがPlan 9環境でもコンパイルされるようになります。--- a/src/pkg/crypto/rand/rand_unix.go +++ b/src/pkg/crypto/rand/rand_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd +// +build darwin freebsd linux netbsd openbsd plan9
-
init()
関数でのrand.Reader
の条件付き初期化:init()
関数は、パッケージがロードされる際に自動的に実行されます。この関数内で、rand.Reader
の初期化方法がruntime.GOOS
(現在のオペレーティングシステム) に応じて分岐されるようになりました。runtime.GOOS == "plan9"
の場合:Reader = newReader(nil)
が使用されます。newReader
は、/dev/urandom
が利用できないシステム向けの代替乱数ジェネレータを生成します。nil
を渡すことで、システムエントロピー源に依存しない(または内部で適切なフォールバックを持つ)ソフトウェアベースのPRNGが初期化されることを示唆しています。- それ以外の場合(Unix系OS): 従来通り
Reader = &devReader{name: "/dev/urandom"}
が使用され、/dev/urandom
から乱数を読み取ります。
--- a/src/pkg/crypto/rand/rand_unix.go +++ b/src/pkg/crypto/rand/rand_unix.go @@ -22,7 +23,13 @@ import ( // Easy implementation: read from /dev/urandom. // This is sufficient on Linux, OS X, and FreeBSD. -func init() { Reader = &devReader{name: "/dev/urandom"} } +func init() { +\tif runtime.GOOS == "plan9" { +\t\tReader = newReader(nil) +\t} else { +\t\tReader = &devReader{name: "/dev/urandom"}\ +\t}\ +}
-
devReader.Read
メソッドでのbufio.Reader
の条件付き適用:devReader
は、指定されたファイル(通常は/dev/urandom
)からバイトを読み取る構造体です。そのRead
メソッド内で、ファイルディスクリプタf
をbufio.NewReader(f)
でラップするかどうかがruntime.GOOS
に応じて分岐されるようになりました。runtime.GOOS == "plan9"
の場合:r.f = f
となり、os.File
オブジェクトが直接r.f
に代入されます。これは、Plan 9のファイルI/Oがバッファリングを必要としないか、あるいはbufio.Reader
を使用すると問題が発生する可能性があることを示唆しています。Plan 9のファイルシステムは、Unixとは異なるセマンティクスを持つため、直接ファイルディスクリプタを扱う方が適切である場合があります。- それ以外の場合:
r.f = bufio.NewReader(f)
となり、bufio.Reader
を介してバッファリングされた読み取りが行われます。これは、Unix系システムでの/dev/urandom
からの読み取り効率を向上させるためです。
--- a/src/pkg/crypto/rand/rand_unix.go +++ b/src/pkg/crypto/rand/rand_unix.go @@ -39,14 +46,17 @@ func (r *devReader) Read(b []byte) (n int, err error) { \t\tif f == nil {\ \t\t\treturn 0, err\ \t\t}\ -\t\tr.f = bufio.NewReader(f)\ +\t\tif runtime.GOOS == "plan9" {\ +\t\t\tr.f = f\ +\t\t} else {\ +\t\t\tr.f = bufio.NewReader(f)\ +\t\t}\ \t}\ \treturn r.f.Read(b)\ }\
これらの変更により、Goの crypto/rand
パッケージはPlan 9環境で適切に動作するようになり、Plan 9上で動作するGoアプリケーションがセキュアな乱数を生成できるようになります。
コアとなるコードの変更箇所
変更は src/pkg/crypto/rand/rand_unix.go
ファイルに集中しています。
-
ビルドタグの変更:
// -build darwin freebsd linux netbsd openbsd // +build darwin freebsd linux netbsd openbsd plan9
この変更により、
rand_unix.go
がPlan 9向けにコンパイルされるようになります。 -
init()
関数の変更:func init() { if runtime.GOOS == "plan9" { Reader = newReader(nil) // Plan 9では代替の乱数ジェネレータを使用 } else { Reader = &devReader{name: "/dev/urandom"} // それ以外のUnix系OSでは/dev/urandomを使用 } }
rand.Reader
の初期化ロジックがOSによって分岐されます。 -
devReader.Read
メソッド内のファイルリーダー初期化の変更:if f == nil { return 0, err } if runtime.GOOS == "plan9" { r.f = f // Plan 9では直接os.Fileを使用 } else { r.f = bufio.NewReader(f) // それ以外のUnix系OSではbufio.Readerでラップ }
devReader
が内部で使用するファイルリーダーの型がOSによって分岐されます。
コアとなるコードの解説
このコミットの核心は、Goのクロスプラットフォーム対応におけるOS固有の挙動の吸収です。
-
ビルドタグの追加: これは、GoのビルドシステムがこのファイルをPlan 9のビルドに含めるための最も基本的なステップです。これにより、Plan 9環境で
crypto/rand
パッケージが利用可能になります。 -
init()
関数での条件分岐:crypto/rand
パッケージは、rand.Reader
というグローバルなio.Reader
インターフェースを提供します。このReader
は、パッケージが初期化される際に、システムから乱数を読み取るための具体的な実装に設定されます。 Unix系システムでは、/dev/urandom
が乱数源として非常に信頼性が高く、効率的です。そのため、devReader
という構造体が/dev/urandom
を開いて読み取るように設定されます。 しかし、Plan 9には/dev/urandom
が存在しないため、このアプローチは使えません。そこで、runtime.GOOS == "plan9"
の場合にnewReader(nil)
が呼び出されます。このnewReader
関数は、おそらくPlan 9のシステムコールやファイルシステムを通じて利用可能なエントロピー源(もしあれば)を利用するか、あるいは完全にソフトウェアベースで乱数を生成するフォールバックメカニズムを提供します。nil
を引数として渡すことで、newReader
が内部で適切なデフォルトのエントロピー源を見つけるか、あるいは自己シード型の擬似乱数ジェネレータとして動作することを示唆しています。 -
devReader.Read
メソッドでのbufio.Reader
の条件分岐:devReader
は、実際にファイルからバイトを読み取るロジックをカプセル化しています。Unix系システムでは、/dev/urandom
からの読み取りは、小さなチャンクで頻繁に行われる可能性があります。このような場合、bufio.NewReader
を使用して読み取りをバッファリングすることで、システムコール(ファイルI/O)の回数を減らし、パフォーマンスを向上させることができます。 一方、Plan 9では、ファイルI/Oのセマンティクスが異なるため、bufio.Reader
を介したバッファリングが必ずしも適切でないか、あるいは不要である可能性があります。Plan 9のファイルシステムは、ネットワーク透過性や分散性を重視しており、ファイルディスクリプタを直接扱う方が効率的であるか、あるいは予期せぬ挙動を避けるために推奨される場合があります。この変更は、Plan 9のI/Oモデルに合わせた最適化、または互換性確保のための調整と考えられます。
このコミットは、Go言語が異なるOSの特性を考慮し、それぞれのプラットフォームで最適なパフォーマンスと正しい動作を保証するための典型的な例を示しています。
関連リンク
- Go言語の
crypto/rand
パッケージのドキュメント: https://pkg.go.dev/crypto/rand - Go言語の
runtime
パッケージのドキュメント: https://pkg.go.dev/runtime - Go言語の
bufio
パッケージのドキュメント: https://pkg.go.dev/bufio - Plan 9 from Bell Labs 公式サイト: https://9p.io/plan9/
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/crypto/rand/
) - Unix系OSにおける
/dev/urandom
の概念に関する一般的な情報 - Plan 9オペレーティングシステムの設計原則に関する一般的な情報
- Go言語のビルドタグに関するドキュメント
- Go言語の
io
パッケージとbufio
パッケージに関するドキュメント - Go CL 6297044: https://golang.org/cl/6297044 (コミットメッセージに記載されている変更リストへのリンク)