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  }