init.go 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package seed
  2. import (
  3. crand "crypto/rand"
  4. "fmt"
  5. "math"
  6. "math/big"
  7. "math/rand"
  8. "sync"
  9. "sync/atomic"
  10. "time"
  11. )
  12. var (
  13. m sync.Mutex
  14. secure int32
  15. seeded int32
  16. )
  17. func cryptoSeed() error {
  18. defer atomic.StoreInt32(&seeded, 1)
  19. var err error
  20. var n *big.Int
  21. n, err = crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
  22. if err != nil {
  23. rand.Seed(time.Now().UTC().UnixNano())
  24. return err
  25. }
  26. rand.Seed(n.Int64())
  27. atomic.StoreInt32(&secure, 1)
  28. return nil
  29. }
  30. // Init provides best-effort seeding (which is better than running with Go's
  31. // default seed of 1). If `/dev/urandom` is available, Init() will seed Go's
  32. // runtime with entropy from `/dev/urandom` and return true because the runtime
  33. // was securely seeded. If Init() has already initialized the random number or
  34. // it had failed to securely initialize the random number generation, Init()
  35. // will return false. See MustInit().
  36. func Init() (seededSecurely bool, err error) {
  37. if atomic.LoadInt32(&seeded) == 1 {
  38. return false, nil
  39. }
  40. // Slow-path
  41. m.Lock()
  42. defer m.Unlock()
  43. if err := cryptoSeed(); err != nil {
  44. return false, err
  45. }
  46. return true, nil
  47. }
  48. // MustInit provides guaranteed secure seeding. If `/dev/urandom` is not
  49. // available, MustInit will panic() with an error indicating why reading from
  50. // `/dev/urandom` failed. MustInit() will upgrade the seed if for some reason a
  51. // call to Init() failed in the past.
  52. func MustInit() {
  53. if atomic.LoadInt32(&secure) == 1 {
  54. return
  55. }
  56. // Slow-path
  57. m.Lock()
  58. defer m.Unlock()
  59. if err := cryptoSeed(); err != nil {
  60. panic(fmt.Sprintf("Unable to seed the random number generator: %v", err))
  61. }
  62. }
  63. // Secure returns true if a cryptographically secure seed was used to
  64. // initialize rand.
  65. func Secure() bool {
  66. return atomic.LoadInt32(&secure) == 1
  67. }
  68. // Seeded returns true if Init has seeded the random number generator.
  69. func Seeded() bool {
  70. return atomic.LoadInt32(&seeded) == 1
  71. }