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  }