mirror of
https://github.com/refraction-networking/utls.git
synced 2025-03-31 10:37:36 +03:00
* sync: Go 1.21rc3, QUIC support added (#207) * sync: merge with upstream tag/go-1.21rc3 (#11) * fix: all tests pass * impl: UQUIC Transport * deps: bump up min Go version * new: uquic * fix: add QUICTransportParameter * deprecated: Go 1.19 no longer supported Go 1.19 will fail to build or pass the test once we bump up to the new version. * sync: crypto/tls: restrict RSA keys in certificates to <= 8192 bits (#209) * [release-branch.go1.21] crypto/tls: restrict RSA keys in certificates to <= 8192 bits Extremely large RSA keys in certificate chains can cause a client/server to expend significant CPU time verifying signatures. Limit this by restricting the size of RSA keys transmitted during handshakes to <= 8192 bits. Based on a survey of publicly trusted RSA keys, there are currently only three certificates in circulation with keys larger than this, and all three appear to be test certificates that are not actively deployed. It is possible there are larger keys in use in private PKIs, but we target the web PKI, so causing breakage here in the interests of increasing the default safety of users of crypto/tls seems reasonable. Thanks to Mateusz Poliwczak for reporting this issue. Fixes CVE-2023-29409 * build: [ci skip] boring not included * fix: typo [ci skip] * docs: replenish readme [ci skip] replace old build status badge with new ones, bump up required version noted in docs, update developer contact to reflect current status.
188 lines
4.6 KiB
Go
188 lines
4.6 KiB
Go
/*
|
|
* 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 "crypto/rand"
|
|
"encoding/binary"
|
|
"io"
|
|
"math"
|
|
"math/rand"
|
|
"sync"
|
|
|
|
"golang.org/x/crypto/hkdf"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
const (
|
|
PRNGSeedLength = 32
|
|
)
|
|
|
|
// PRNGSeed is a PRNG seed.
|
|
type PRNGSeed [PRNGSeedLength]byte
|
|
|
|
// NewPRNGSeed creates a new PRNG seed using crypto/rand.Read.
|
|
func NewPRNGSeed() (*PRNGSeed, error) {
|
|
seed := new(PRNGSeed)
|
|
_, err := crypto_rand.Read(seed[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return seed, 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 newSaltedPRNGSeed(seed *PRNGSeed, salt string) (*PRNGSeed, error) {
|
|
saltedSeed := new(PRNGSeed)
|
|
_, err := io.ReadFull(
|
|
hkdf.New(sha3.New256, seed[:], []byte(salt), nil), saltedSeed[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return saltedSeed, 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 newPRNG() (*prng, error) {
|
|
seed, err := NewPRNGSeed()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newPRNGWithSeed(seed)
|
|
}
|
|
|
|
// newPRNGWithSeed initializes a new PRNG using an existing seed.
|
|
func newPRNGWithSeed(seed *PRNGSeed) (*prng, error) {
|
|
shake := sha3.NewShake256()
|
|
_, err := shake.Write(seed[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p := &prng{
|
|
randomStream: shake,
|
|
}
|
|
p.rand = rand.New(p)
|
|
return p, nil
|
|
}
|
|
|
|
// newPRNGWithSaltedSeed initializes a new PRNG using a seed derived from an
|
|
// existing seed and a salt with NewSaltedSeed.
|
|
func newPRNGWithSaltedSeed(seed *PRNGSeed, salt string) (*prng, error) {
|
|
saltedSeed, err := newSaltedPRNGSeed(seed, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newPRNGWithSeed(saltedSeed)
|
|
}
|
|
|
|
// Read reads random bytes from the PRNG stream into b. Read conforms to
|
|
// io.Reader and always returns len(p), nil.
|
|
func (p *prng) Read(b []byte) (int, error) {
|
|
p.randomStreamMutex.Lock()
|
|
defer p.randomStreamMutex.Unlock()
|
|
|
|
// ShakeHash.Read never returns an error:
|
|
// https://godoc.org/golang.org/x/crypto/sha3#ShakeHash
|
|
_, _ = io.ReadFull(p.randomStream, b)
|
|
|
|
return len(b), nil
|
|
}
|
|
|
|
// Int63 is equivalent to math/read.Int63.
|
|
func (p *prng) Int63() int64 {
|
|
i := p.Uint64()
|
|
return int64(i & (1<<63 - 1))
|
|
}
|
|
|
|
// Int63 is equivalent to math/read.Uint64.
|
|
func (p *prng) Uint64() uint64 {
|
|
var b [8]byte
|
|
p.Read(b[:])
|
|
return binary.BigEndian.Uint64(b[:])
|
|
}
|
|
|
|
// Seed must exist in order to use a PRNG as a math/rand.Source. This call is
|
|
// not supported and ignored.
|
|
func (p *prng) Seed(_ 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 (p *prng) FlipWeightedCoin(weight float64) bool {
|
|
if weight > 1.0 {
|
|
weight = 1.0
|
|
}
|
|
f := float64(p.Int63()) / float64(math.MaxInt64)
|
|
return f > 1.0-weight
|
|
}
|
|
|
|
// Intn is equivalent to math/read.Intn, except it returns 0 if n <= 0
|
|
// instead of panicking.
|
|
func (p *prng) Intn(n int) int {
|
|
if n <= 0 {
|
|
return 0
|
|
}
|
|
return p.rand.Intn(n)
|
|
}
|
|
|
|
// Int63n is equivalent to math/read.Int63n, except it returns 0 if n <= 0
|
|
// instead of panicking.
|
|
func (p *prng) Int63n(n int64) int64 {
|
|
if n <= 0 {
|
|
return 0
|
|
}
|
|
return p.rand.Int63n(n)
|
|
}
|
|
|
|
// Intn is equivalent to math/read.Perm.
|
|
func (p *prng) Perm(n int) []int {
|
|
return p.rand.Perm(n)
|
|
}
|
|
|
|
// Range selects a random integer in [min, max].
|
|
// If min < 0, min is set to 0. If max < min, min is returned.
|
|
func (p *prng) Range(min, max int) int {
|
|
if min < 0 {
|
|
min = 0
|
|
}
|
|
if max < min {
|
|
return min
|
|
}
|
|
n := p.Intn(max - min + 1)
|
|
n += min
|
|
return n
|
|
}
|