Help us understand the problem. What is going on with this article?

Goのmath/randとcrypto/rand

Goの乱数生成に関する標準パッケージにはmath/randcrypto/randの2つがあります。それぞれmath/randは弱い乱数でcrypto/randは強い乱数なのですが、強い乱数のほうが必ずしも良いというわけではなく、特性やパフォーマンスの違いでユースケースが分かれています。

math/rand

https://golang.org/pkg/math/rand/

math/randパッケージは疑似乱数生成器を実装しています。

乱数はソースによって生成されます。Float64()Int() などのトップレベル関数は、プログラムが実行されるたび、デフォルトの共有ソースを使用し値を生成します。

実行する毎に異なる動作が必要ならSeed()関数を利用してデフォルトソースを初期化します。

デフォルトソースは複数のゴルーチンから並列使用に対して安全です。しかしNewSource()から作られたものはそうではありません。

セキュリティに適した乱数についてはcrypto/randを参照してください。

通常乱数を使用する場合は math/randを利用します。多くの場合time.Now().UnixNano()を乱数のシード値にすることで、実行するたびに異なる数を得ることができます。

example
package main

import (
    "fmt"
    "time"
    "math/rand"
)

//0から99のランダムな数値を表示する
func main() {
    rand.Seed(time.Now().UnixNano())
    fmt.Println(rand.Intn(100))
}

様々な型で乱数を返す関数が用意されており、且つ高速に安定した乱数を得ることができます。

注意点として、トップレベル関数はgoroutineセーフですが、NewSource()で生成したソースはgoroutineセーフではありません。ただし、トップレベル関数がgotoutineセーフなのは内部でロックを取っているだけなので、乱数を取得する際に同様にロックを取ればgroutineセーフになります。

math/randの擬似乱数生成器ではいくつかの数値をもとに、呼び出すたびに組み合わせを変えることで適度に分散した乱数を生成します。( 参考: https://golang.org/src/math/rand/rng.go )元になる数値は一定で、シード値に対応した値を返すため乱数を再現することが可能です。このような無作為な数列になるが、予測が可能な乱数を弱い乱数と呼びます。

ゲームのダイスなどは弱い乱数で十分ですが、暗号化やセッションID生成で利用する場合は脆弱性になり得ます。例えば、セッションIDをmath/randの乱数をもとに文字列生成するときに time.Now().UnixNano() がシード値だった場合、セッションIDを発行した時間帯が判れば総当りでセッションIDを特定できるかもしれません。

math/randは十分に分散した数列を高速に得られますが、予測可能であるため暗号論的に必ずしも安全ではありません。

crypto/rand

https://golang.org/pkg/crypto/rand/

crypto/randパッケージは暗号学的に安全な乱数生成器を実装しています。

暗号学的に安全な疑似乱数生成器はCSPRNG(cryptographically secure pseudo random number generator)とも呼ばれます。

math/randとは異なり、io.Readerインターフェイスを利用してランダムなバイト列を取り出すことができます。

example
package main

import (
    "crypto/rand"
    "fmt"
    "math/big"
)

//0から99のランダムな数値を表示する
func main() {
    n, err := rand.Int(rand.Reader, big.NewInt(100))
    if err != nil {
        panic(err)
    }
    fmt.Println(n)
}

rand.Readerの中身は環境によって異なります。linuxの場合は/dev/urandomを利用し、WindowsであればCryptoAPIを利用します。

crypto/randで利用されるものはすべてCSPRNGです。linuxではデバイスドライバなどから得られた環境ノイズをシードとして乱数を生成します。そのため、プログラムからシード値を与えることはありません。

ただし、/dev/urandomは一般的に重い処理であるため、math/randに比べると環境によってはパフォーマンスが大きく落ちることがあります。

まとめ

抽選や無作為サンプリングなどの十分にランダム性のある数列が求められる場合はmath/randを利用します。

ランダムかつ暗号的な安全さ(予測困難性)が求められる場合はcrypto/randを利用するのが望ましいです。例えば「暗号化に際に必要なランダムなバイト列の生成」や「セッションIDの生成」などが挙げられます。

crifff
最近はgoとかappengineが世界を救うと信じてる
bandainamcostudios
バンダイナムコスタジオは、家庭用ゲームソフト、モバイルコンテンツ、の企画・開発・運営、ゲームに関する技術研究・開発を行っている会社です。
https://www.bandainamcostudios.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした