/*
 * Copyright (c) 2019, Psiphon Inc.
 * All rights reserved.
 *
 * Released under utls licence:
 * https://github.com/refraction-networking/utls/blob/master/LICENSE
 */

// This code is a pared down version of:
// https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/158caea562287284cc3fa5fcd1b3c97b1addf659/psiphon/common/prng/prng.go

package tls

import (
	crypto_rand 
	
	
	
	
	

	
	
)

const (
	PRNGSeedLength = 32
)

// PRNGSeed is a PRNG seed.
type PRNGSeed [PRNGSeedLength]byte

// NewPRNGSeed creates a new PRNG seed using crypto/rand.Read.
func () (*PRNGSeed, error) {
	 := new(PRNGSeed)
	,  := crypto_rand.Read([:])
	if  != nil {
		return nil, 
	}
	return , nil
}

// newSaltedPRNGSeed creates a new seed derived from an existing seed and a
// salt. A HKDF is applied to the seed and salt.
//
// newSaltedPRNGSeed is intended for use cases where a single seed needs to be
// used in distinct contexts to produce independent random streams.
func ( *PRNGSeed,  string) (*PRNGSeed, error) {
	 := new(PRNGSeed)
	,  := io.ReadFull(
		hkdf.New(sha3.New256, [:], []byte(), nil), [:])
	if  != nil {
		return nil, 
	}
	return , nil
}

// prng is a seeded, unbiased PRNG based on SHAKE256. that is suitable for use
// cases such as obfuscation. Seeding is based on crypto/rand.Read.
//
// This PRNG is _not_ for security use cases including production cryptographic
// key generation.
//
// It is safe to make concurrent calls to a PRNG instance.
//
// PRNG conforms to io.Reader and math/rand.Source, with additional helper
// functions.
type prng struct {
	rand              *rand.Rand
	randomStreamMutex sync.Mutex
	randomStream      sha3.ShakeHash
}

// newPRNG generates a seed and creates a PRNG with that seed.
func () (*prng, error) {
	,  := NewPRNGSeed()
	if  != nil {
		return nil, 
	}
	return newPRNGWithSeed()
}

// newPRNGWithSeed initializes a new PRNG using an existing seed.
func ( *PRNGSeed) (*prng, error) {
	 := sha3.NewShake256()
	,  := .Write([:])
	if  != nil {
		return nil, 
	}
	 := &prng{
		randomStream: ,
	}
	.rand = rand.New()
	return , nil
}

// newPRNGWithSaltedSeed initializes a new PRNG using a seed derived from an
// existing seed and a salt with NewSaltedSeed.
func ( *PRNGSeed,  string) (*prng, error) {
	,  := newSaltedPRNGSeed(, )
	if  != nil {
		return nil, 
	}
	return newPRNGWithSeed()
}

// Read reads random bytes from the PRNG stream into b. Read conforms to
// io.Reader and always returns len(p), nil.
func ( *prng) ( []byte) (int, error) {
	.randomStreamMutex.Lock()
	defer .randomStreamMutex.Unlock()

	// ShakeHash.Read never returns an error:
	// https://godoc.org/golang.org/x/crypto/sha3#ShakeHash
	_, _ = io.ReadFull(.randomStream, )

	return len(), nil
}

// Int63 is equivalent to math/read.Int63.
func ( *prng) () int64 {
	 := .Uint64()
	return int64( & (1<<63 - 1))
}

// Int63 is equivalent to math/read.Uint64.
func ( *prng) () uint64 {
	var  [8]byte
	.Read([:])
	return binary.BigEndian.Uint64([:])
}

// Seed must exist in order to use a PRNG as a math/rand.Source. This call is
// not supported and ignored.
func ( *prng) ( int64) {
}

// FlipWeightedCoin returns the result of a weighted
// random coin flip. If the weight is 0.5, the outcome
// is equally likely to be true or false. If the weight
// is 1.0, the outcome is always true, and if the
// weight is 0.0, the outcome is always false.
//
// Input weights > 1.0 are treated as 1.0.
func ( *prng) ( float64) bool {
	if  > 1.0 {
		 = 1.0
	}
	 := float64(.Int63()) / float64(math.MaxInt64)
	return  > 1.0-
}

// Intn is equivalent to math/read.Intn, except it returns 0 if n <= 0
// instead of panicking.
func ( *prng) ( int) int {
	if  <= 0 {
		return 0
	}
	return .rand.Intn()
}

// Int63n is equivalent to math/read.Int63n, except it returns 0 if n <= 0
// instead of panicking.
func ( *prng) ( int64) int64 {
	if  <= 0 {
		return 0
	}
	return .rand.Int63n()
}

// Intn is equivalent to math/read.Perm.
func ( *prng) ( int) []int {
	return .rand.Perm()
}

// Range selects a random integer in [min, max].
// If min < 0, min is set to 0. If max < min, min is returned.
func ( *prng) (,  int) int {
	if  < 0 {
		 = 0
	}
	if  <  {
		return 
	}
	 := .Intn( -  + 1)
	 += 
	return 
}