u_prng.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * Copyright (c) 2019, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * Released under utls licence:
  6. * https://github.com/refraction-networking/utls/blob/master/LICENSE
  7. */
  8. // This code is a pared down version of:
  9. // https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/158caea562287284cc3fa5fcd1b3c97b1addf659/psiphon/common/prng/prng.go
  10. package tls
  11. import (
  12. crypto_rand "crypto/rand"
  13. "encoding/binary"
  14. "io"
  15. "math"
  16. "math/rand"
  17. "sync"
  18. "golang.org/x/crypto/hkdf"
  19. "golang.org/x/crypto/sha3"
  20. )
  21. const (
  22. PRNGSeedLength = 32
  23. )
  24. // PRNGSeed is a PRNG seed.
  25. type PRNGSeed [PRNGSeedLength]byte
  26. // NewPRNGSeed creates a new PRNG seed using crypto/rand.Read.
  27. func NewPRNGSeed() (*PRNGSeed, error) {
  28. seed := new(PRNGSeed)
  29. _, err := crypto_rand.Read(seed[:])
  30. if err != nil {
  31. return nil, err
  32. }
  33. return seed, nil
  34. }
  35. // newSaltedPRNGSeed creates a new seed derived from an existing seed and a
  36. // salt. A HKDF is applied to the seed and salt.
  37. //
  38. // newSaltedPRNGSeed is intended for use cases where a single seed needs to be
  39. // used in distinct contexts to produce independent random streams.
  40. func newSaltedPRNGSeed(seed *PRNGSeed, salt string) (*PRNGSeed, error) {
  41. saltedSeed := new(PRNGSeed)
  42. _, err := io.ReadFull(
  43. hkdf.New(sha3.New256, seed[:], []byte(salt), nil), saltedSeed[:])
  44. if err != nil {
  45. return nil, err
  46. }
  47. return saltedSeed, nil
  48. }
  49. // prng is a seeded, unbiased PRNG based on SHAKE256. that is suitable for use
  50. // cases such as obfuscation. Seeding is based on crypto/rand.Read.
  51. //
  52. // This PRNG is _not_ for security use cases including production cryptographic
  53. // key generation.
  54. //
  55. // It is safe to make concurrent calls to a PRNG instance.
  56. //
  57. // PRNG conforms to io.Reader and math/rand.Source, with additional helper
  58. // functions.
  59. type prng struct {
  60. rand *rand.Rand
  61. randomStreamMutex sync.Mutex
  62. randomStream sha3.ShakeHash
  63. }
  64. // newPRNG generates a seed and creates a PRNG with that seed.
  65. func newPRNG() (*prng, error) {
  66. seed, err := NewPRNGSeed()
  67. if err != nil {
  68. return nil, err
  69. }
  70. return newPRNGWithSeed(seed)
  71. }
  72. // newPRNGWithSeed initializes a new PRNG using an existing seed.
  73. func newPRNGWithSeed(seed *PRNGSeed) (*prng, error) {
  74. shake := sha3.NewShake256()
  75. _, err := shake.Write(seed[:])
  76. if err != nil {
  77. return nil, err
  78. }
  79. p := &prng{
  80. randomStream: shake,
  81. }
  82. p.rand = rand.New(p)
  83. return p, nil
  84. }
  85. // newPRNGWithSaltedSeed initializes a new PRNG using a seed derived from an
  86. // existing seed and a salt with NewSaltedSeed.
  87. func newPRNGWithSaltedSeed(seed *PRNGSeed, salt string) (*prng, error) {
  88. saltedSeed, err := newSaltedPRNGSeed(seed, salt)
  89. if err != nil {
  90. return nil, err
  91. }
  92. return newPRNGWithSeed(saltedSeed)
  93. }
  94. // Read reads random bytes from the PRNG stream into b. Read conforms to
  95. // io.Reader and always returns len(p), nil.
  96. func (p *prng) Read(b []byte) (int, error) {
  97. p.randomStreamMutex.Lock()
  98. defer p.randomStreamMutex.Unlock()
  99. // ShakeHash.Read never returns an error:
  100. // https://godoc.org/golang.org/x/crypto/sha3#ShakeHash
  101. _, _ = io.ReadFull(p.randomStream, b)
  102. return len(b), nil
  103. }
  104. // Int63 is equivalent to math/read.Int63.
  105. func (p *prng) Int63() int64 {
  106. i := p.Uint64()
  107. return int64(i & (1<<63 - 1))
  108. }
  109. // Int63 is equivalent to math/read.Uint64.
  110. func (p *prng) Uint64() uint64 {
  111. var b [8]byte
  112. p.Read(b[:])
  113. return binary.BigEndian.Uint64(b[:])
  114. }
  115. // Seed must exist in order to use a PRNG as a math/rand.Source. This call is
  116. // not supported and ignored.
  117. func (p *prng) Seed(_ int64) {
  118. }
  119. // FlipWeightedCoin returns the result of a weighted
  120. // random coin flip. If the weight is 0.5, the outcome
  121. // is equally likely to be true or false. If the weight
  122. // is 1.0, the outcome is always true, and if the
  123. // weight is 0.0, the outcome is always false.
  124. //
  125. // Input weights > 1.0 are treated as 1.0.
  126. func (p *prng) FlipWeightedCoin(weight float64) bool {
  127. if weight > 1.0 {
  128. weight = 1.0
  129. }
  130. f := float64(p.Int63()) / float64(math.MaxInt64)
  131. return f > 1.0-weight
  132. }
  133. // Intn is equivalent to math/read.Intn, except it returns 0 if n <= 0
  134. // instead of panicking.
  135. func (p *prng) Intn(n int) int {
  136. if n <= 0 {
  137. return 0
  138. }
  139. return p.rand.Intn(n)
  140. }
  141. // Int63n is equivalent to math/read.Int63n, except it returns 0 if n <= 0
  142. // instead of panicking.
  143. func (p *prng) Int63n(n int64) int64 {
  144. if n <= 0 {
  145. return 0
  146. }
  147. return p.rand.Int63n(n)
  148. }
  149. // Intn is equivalent to math/read.Perm.
  150. func (p *prng) Perm(n int) []int {
  151. return p.rand.Perm(n)
  152. }
  153. // Range selects a random integer in [min, max].
  154. // If min < 0, min is set to 0. If max < min, min is returned.
  155. func (p *prng) Range(min, max int) int {
  156. if min < 0 {
  157. min = 0
  158. }
  159. if max < min {
  160. return min
  161. }
  162. n := p.Intn(max - min + 1)
  163. n += min
  164. return n
  165. }