github.com/mailgun/holster/v4@v4.20.0/httpsign/random.go (about) 1 package httpsign 2 3 import ( 4 "crypto/rand" 5 "encoding/hex" 6 "io" 7 "math" 8 math_rand "math/rand" 9 ) 10 11 // Interface for our random number generator. We need this 12 // to fake random values in tests. 13 type randomProvider interface { 14 bytes(bytes int) ([]byte, error) 15 hexDigest(bytes int) (string, error) 16 } 17 18 // Real random values, used in production 19 type realRandom struct{} 20 21 // Return n-bytes of random values from the realRandom. 22 func (c *realRandom) bytes(bytes int) ([]byte, error) { 23 n := make([]byte, bytes) 24 25 // get bytes-bit random number from /dev/urandom 26 _, err := io.ReadFull(rand.Reader, n) 27 if err != nil { 28 return nil, err 29 } 30 31 return n, nil 32 } 33 34 // Return n-bytes of random values from the realRandom but as a 35 // hex-encoded (base16) string. 36 func (c *realRandom) hexDigest(bytes int) (string, error) { 37 return hexDigest(c, bytes) 38 } 39 40 // Fake random, used in tests. never use this in production! 41 type fakeRandom struct{} 42 43 // Fake random number generator, never use in production. Always 44 // returns a predictable sequence of bytes that looks like: 0x00, 45 // 0x01, 0x02, 0x03, ... 46 func (f *fakeRandom) bytes(bytes int) ([]byte, error) { 47 // create bytes long array 48 b := make([]byte, bytes) 49 50 for i := 0; i < len(b); i++ { 51 b[i] = byte(i) 52 } 53 54 return b, nil 55 } 56 57 // Fake random number generator, never use in production. Always returns 58 // a predictable hex-encoded (base16) string that looks like "00010203..." 59 func (f *fakeRandom) hexDigest(bytes int) (string, error) { 60 return hexDigest(f, bytes) 61 } 62 63 // SeededRNG returns bytes generated in a predictable sequence by package math/rand. 64 // Not cryptographically secure, not thread safe. 65 // Changes to Seed after the first call to Bytes or HexDigest 66 // will have no effect. The zero value of SeededRNG is ready to use, 67 // and will use a seed of 0. 68 type SeededRNG struct { 69 Seed int64 70 rand *math_rand.Rand 71 } 72 73 // Bytes conforms to the randomProvider interface. Returns bytes 74 // generated by a math/rand.Rand. 75 func (r *SeededRNG) bytes(bytes int) ([]byte, error) { 76 if r.rand == nil { 77 //nolint: gosec // Use of math/rand is indended. 78 r.rand = math_rand.New(math_rand.NewSource(r.Seed)) 79 } 80 b := make([]byte, bytes) 81 for i := range b { 82 b[i] = byte(r.rand.Intn(math.MaxUint8 + 1)) 83 } 84 return b, nil 85 } 86 87 // HexDigest conforms to the randomProvider interface. Returns 88 // a hex encoding of bytes generated by a math/rand.Rand. 89 func (r *SeededRNG) hexDigest(bytes int) (string, error) { 90 return hexDigest(r, bytes) 91 } 92 93 func hexDigest(r randomProvider, bytes int) (string, error) { 94 b, err := r.bytes(bytes) 95 if err != nil { 96 return "", err 97 } 98 return hex.EncodeToString(b), nil 99 }