package seed import ( crand "crypto/rand" "fmt" "math" "math/big" "math/rand" "sync" "sync/atomic" "time" ) var ( m sync.Mutex secure int32 seeded int32 ) func cryptoSeed() error { defer atomic.StoreInt32(&seeded, 1) var err error var n *big.Int n, err = crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) if err != nil { rand.Seed(time.Now().UTC().UnixNano()) return err } rand.Seed(n.Int64()) atomic.StoreInt32(&secure, 1) return nil } // Init provides best-effort seeding (which is better than running with Go's // default seed of 1). If `/dev/urandom` is available, Init() will seed Go's // runtime with entropy from `/dev/urandom` and return true because the runtime // was securely seeded. If Init() has already initialized the random number or // it had failed to securely initialize the random number generation, Init() // will return false. See MustInit(). func Init() (seededSecurely bool, err error) { if atomic.LoadInt32(&seeded) == 1 { return false, nil } // Slow-path m.Lock() defer m.Unlock() if err := cryptoSeed(); err != nil { return false, err } return true, nil } // MustInit provides guaranteed secure seeding. If `/dev/urandom` is not // available, MustInit will panic() with an error indicating why reading from // `/dev/urandom` failed. MustInit() will upgrade the seed if for some reason a // call to Init() failed in the past. func MustInit() { if atomic.LoadInt32(&secure) == 1 { return } // Slow-path m.Lock() defer m.Unlock() if err := cryptoSeed(); err != nil { panic(fmt.Sprintf("Unable to seed the random number generator: %v", err)) } } // Secure returns true if a cryptographically secure seed was used to // initialize rand. func Secure() bool { return atomic.LoadInt32(&secure) == 1 } // Seeded returns true if Init has seeded the random number generator. func Seeded() bool { return atomic.LoadInt32(&seeded) == 1 }