random.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. // Package rand implements functions similar to math/rand in the standard
  7. // library, but on top of a secure random number generator.
  8. package rand
  9. import (
  10. "crypto/md5"
  11. cryptoRand "crypto/rand"
  12. "encoding/binary"
  13. "io"
  14. mathRand "math/rand"
  15. "reflect"
  16. )
  17. // Reader is the standard crypto/rand.Reader, re-exported for convenience
  18. var Reader = cryptoRand.Reader
  19. // randomCharset contains the characters that can make up a randomString().
  20. const randomCharset = "2345679abcdefghijkmnopqrstuvwxyzACDEFGHJKLMNPQRSTUVWXYZ"
  21. var (
  22. // defaultSecureSource is a concurrency safe math/rand.Source with a
  23. // cryptographically sound base.
  24. defaltSecureSource = newSecureSource()
  25. // defaultSecureRand is a math/rand.Rand based on the secure source.
  26. defaultSecureRand = mathRand.New(defaltSecureSource)
  27. )
  28. // String returns a strongly random string of characters (taken from
  29. // randomCharset) of the specified length. The returned string contains ~5.8
  30. // bits of entropy per character, due to the character set used.
  31. func String(l int) string {
  32. bs := make([]byte, l)
  33. for i := range bs {
  34. bs[i] = randomCharset[defaultSecureRand.Intn(len(randomCharset))]
  35. }
  36. return string(bs)
  37. }
  38. // Int63 returns a strongly random int63
  39. func Int63() int64 {
  40. return defaltSecureSource.Int63()
  41. }
  42. // Int64 returns a strongly random int64
  43. func Int64() int64 {
  44. var bs [8]byte
  45. _, err := io.ReadFull(cryptoRand.Reader, bs[:])
  46. if err != nil {
  47. panic("randomness failure: " + err.Error())
  48. }
  49. return int64(binary.BigEndian.Uint64(bs[:]))
  50. }
  51. // Intn returns, as an int, a non-negative strongly random number in [0,n).
  52. // It panics if n <= 0.
  53. func Intn(n int) int {
  54. return defaultSecureRand.Intn(n)
  55. }
  56. // SeedFromBytes calculates a weak 64 bit hash from the given byte slice,
  57. // suitable for use a predictable random seed.
  58. func SeedFromBytes(bs []byte) int64 {
  59. h := md5.New()
  60. h.Write(bs)
  61. s := h.Sum(nil)
  62. // The MD5 hash of the byte slice is 16 bytes long. We interpret it as two
  63. // uint64s and XOR them together.
  64. return int64(binary.BigEndian.Uint64(s[0:]) ^ binary.BigEndian.Uint64(s[8:]))
  65. }
  66. // Shuffle the order of elements
  67. func Shuffle(slice interface{}) {
  68. rv := reflect.ValueOf(slice)
  69. swap := reflect.Swapper(slice)
  70. length := rv.Len()
  71. if length < 2 {
  72. return
  73. }
  74. defaultSecureRand.Shuffle(length, swap)
  75. }