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