mirror of
https://github.com/xtaci/kcp-go.git
synced 2026-10-19 16:14:38 +00:00
- pkg.go: fix grammar in package doc (intents→intends, aims→aim, golang→Go) - snmp.go: fix typos (iput→input); clarify FECShardSet and FECShardMin field descriptions - kcp_trace_off.go: fix typo (ingore→no-op) - readloop_linux.go: improve readLoop doc comment; document batchSize constant - tx_linux.go: improve tx() doc comment - entropy.go: document reseedInterval constant No code logic changes. Comments and documentation only.
172 lines
4.0 KiB
Go
172 lines
4.0 KiB
Go
// The MIT License (MIT)
|
|
//
|
|
// Copyright (c) 2015 xtaci
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
package kcp
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
crand "crypto/rand"
|
|
"io"
|
|
"math/rand/v2"
|
|
"runtime"
|
|
"sync"
|
|
|
|
"golang.org/x/sys/cpu"
|
|
)
|
|
|
|
const reseedInterval = 1 << 24 // reseed after ~16M reads to limit key exposure
|
|
|
|
var (
|
|
hasAESAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
|
|
hasAESAsmARM64 = cpu.ARM64.HasAES
|
|
hasAESAsmS390X = cpu.S390X.HasAES
|
|
hasAESAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
|
|
|
|
hasAESHardwareSupport = hasAESAsmAMD64 || hasAESAsmARM64 || hasAESAsmS390X || hasAESAsmPPC64
|
|
|
|
entropy io.Reader = NewEntropy()
|
|
)
|
|
|
|
// NewEntropy creates a new entropy source.
|
|
func NewEntropy() io.Reader {
|
|
if hasAESHardwareSupport {
|
|
return NewEntropyAES()
|
|
}
|
|
|
|
return NewEntropyChacha8()
|
|
}
|
|
|
|
// SetEntropy sets the global entropy source used by fillRand.
|
|
func SetEntropy(r io.Reader) {
|
|
entropy = r
|
|
}
|
|
|
|
// fillRand fills p with random data from the global entropy source.
|
|
func fillRand(p []byte) {
|
|
if len(p) <= 0 {
|
|
return
|
|
}
|
|
io.ReadFull(entropy, p)
|
|
}
|
|
|
|
// rngAES is an AES-based random number generator.
|
|
type rngAES struct {
|
|
mutex sync.Mutex
|
|
block cipher.Block
|
|
seed [16]byte
|
|
count uint64
|
|
}
|
|
|
|
// NewEntropyAES creates a new AES-based entropy source.
|
|
func NewEntropyAES() io.Reader {
|
|
r := new(rngAES)
|
|
|
|
var key [16]byte
|
|
io.ReadFull(crand.Reader, key[:])
|
|
io.ReadFull(crand.Reader, r.seed[:])
|
|
|
|
block, err := aes.NewCipher(key[:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
r.block = block
|
|
return r
|
|
}
|
|
|
|
// updateSeed updates the AES seed after a certain number of reads.
|
|
func (r *rngAES) updateSeed() {
|
|
if r.count < reseedInterval {
|
|
r.count++
|
|
return
|
|
}
|
|
|
|
var key [16]byte
|
|
io.ReadFull(crand.Reader, key[:])
|
|
io.ReadFull(crand.Reader, r.seed[:])
|
|
|
|
block, err := aes.NewCipher(key[:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
r.block = block
|
|
r.count = 0
|
|
}
|
|
|
|
// Read fills p with random data using AES encryption.
|
|
func (r *rngAES) Read(p []byte) (int, error) {
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
r.mutex.Lock()
|
|
r.updateSeed()
|
|
r.block.Encrypt(r.seed[:], r.seed[:])
|
|
n := copy(p, r.seed[:])
|
|
r.mutex.Unlock()
|
|
return n, nil
|
|
}
|
|
|
|
// rngChacha8 is a ChaCha8-based random number generator.
|
|
type rngChacha8 struct {
|
|
mutex sync.Mutex
|
|
rand *rand.ChaCha8
|
|
count uint64
|
|
}
|
|
|
|
// NewEntropyChacha8 creates a new ChaCha8-based entropy source.
|
|
func NewEntropyChacha8() io.Reader {
|
|
var seed [32]byte
|
|
io.ReadFull(crand.Reader, seed[:])
|
|
|
|
return &rngChacha8{
|
|
rand: rand.NewChaCha8(seed),
|
|
}
|
|
}
|
|
|
|
// updateSeed updates the ChaCha8 seed after a certain number of reads.
|
|
func (r *rngChacha8) updateSeed() {
|
|
if r.count < reseedInterval {
|
|
r.count++
|
|
return
|
|
}
|
|
|
|
var seed [32]byte
|
|
io.ReadFull(crand.Reader, seed[:])
|
|
|
|
r.rand.Seed(seed)
|
|
r.count = 0
|
|
}
|
|
|
|
// Read fills p with random data using ChaCha8.
|
|
func (r *rngChacha8) Read(p []byte) (int, error) {
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
r.mutex.Lock()
|
|
r.updateSeed()
|
|
n, err := r.rand.Read(p)
|
|
r.mutex.Unlock()
|
|
return n, err
|
|
}
|