package unsafe import ( crand "crypto/rand" mrand "math/rand" "sync" ) const ( strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters ) // Rand is a prng, that is seeded with OS randomness. // The OS randomness is obtained from crypto/rand, however none of the provided // methods are suitable for cryptographic usage. // They all utilize math/rand's prng internally. // // All of the methods here are suitable for concurrent use. // This is achieved by using a mutex lock on all of the provided methods. type Rand struct { sync.Mutex rand *mrand.Rand } var grand *Rand func init() { grand = NewRand() } func NewRand() *Rand { rand := &Rand{} rand.init() return rand } func (r *Rand) init() { bz := cRandBytes(8) var seed uint64 for i := 0; i < 8; i++ { seed |= uint64(bz[i]) seed <<= 8 } r.reset(int64(seed)) } func (r *Rand) reset(seed int64) { r.rand = mrand.New(mrand.NewSource(seed)) } //---------------------------------------- // Global functions func Seed(seed int64) { grand.Seed(seed) } func Str(length int) string { return grand.Str(length) } func Int63() int64 { return grand.Int63() } func Int() int { return grand.Int() } func Bytes(n int) []byte { return grand.Bytes(n) } //---------------------------------------- // Rand methods func (r *Rand) Seed(seed int64) { r.Lock() r.reset(seed) r.Unlock() } // Str constructs a random alphanumeric string of given length. func (r *Rand) Str(length int) string { if length <= 0 { return "" } chars := []byte{} MAIN_LOOP: for { val := r.Int63() for i := 0; i < 10; i++ { v := int(val & 0x3f) // rightmost 6 bits if v >= 62 { // only 62 characters in strChars val >>= 6 continue } else { chars = append(chars, strChars[v]) if len(chars) == length { break MAIN_LOOP } val >>= 6 } } } return string(chars) } func (r *Rand) Int63() int64 { r.Lock() i63 := r.rand.Int63() r.Unlock() return i63 } func (r *Rand) Int() int { r.Lock() i := r.rand.Int() r.Unlock() return i } // Bytes returns n random bytes generated from the internal // prng. func (r *Rand) Bytes(n int) []byte { // cRandBytes isn't guaranteed to be fast so instead // use random bytes generated from the internal PRNG bs := make([]byte, n) for i := 0; i < len(bs); i++ { bs[i] = byte(r.Int() & 0xFF) } return bs } // NOTE: This relies on the os's random number generator. // For real security, we should salt that with some seed. // See github.com/cometbft/cometbft/crypto for a more secure reader. func cRandBytes(numBytes int) []byte { b := make([]byte, numBytes) _, err := crand.Read(b) if err != nil { panic(err) } return b }