Files
kcp-go/crypt.go
T
xtaci fec60d1ec6 docs: improve code readability with structured comments and section headers
- kcp.go: Group protocol constants by category (RTO, commands, probe flags,
  window/MTU defaults, protocol parameters) with descriptive comments.
  Document KCP struct fields with protocol semantics (RFC 6298/5681/6937).
  Add 6-phase description to flush() and annotate each phase.

- sess.go: Add section separators for session, packet input pipeline, listener,
  and public API. Document postProcess() 3-stage pipeline and kcpInput()
  packet demultiplexing logic.

- fec.go: Document FEC constants (header sizes, packet types, maxShardSets).
  Clarify fecDecoder's auto-tuning mechanism.

- crypt.go: Add section header for CFB-mode encryption. Document encrypt/decrypt
  dispatch and 8x loop unrolling optimization strategy.

- timedsched.go: Document two-stage pipeline architecture (prepend → sched)
  and explain why stages are separated.

- autotune.go: Expand FindPeriod() documentation with algorithm description
  and fix ASCII signal diagram alignment.

- ringbuffer.go: Document RINGBUFFER_MIN and RINGBUFFER_EXP constants.

- snmp.go: Fix typos (accmulated → accumulated, retransmited → retransmitted).

No code logic changes. All modifications are comments only.
2026-03-13 17:01:51 +08:00

697 lines
18 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"
"crypto/des"
"crypto/sha1"
"crypto/subtle"
"sync"
"github.com/tjfoc/gmsm/sm4"
"golang.org/x/crypto/blowfish"
"golang.org/x/crypto/cast5"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/salsa20"
"golang.org/x/crypto/tea"
"golang.org/x/crypto/twofish"
"golang.org/x/crypto/xtea"
)
var (
// a defined initial vector
// https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_.28IV.29
// https://en.wikipedia.org/wiki/Initialization_vector
// actually initial vector is not used in this package, we prepend a random nonce to each outgoing packets.
// though IV is fixed, the first 8 bytes of the encrypted data is always random.
initialVector = []byte{167, 115, 79, 156, 18, 172, 27, 1, 164, 21, 242, 193, 252, 120, 230, 107}
saltxor = `sH3CIVoF#rWLtJo6`
)
// BlockCrypt defines encryption/decryption methods for a given byte slice.
// Notes on implementing: the data to be encrypted contains a builtin
// nonce at the first 16 bytes
type BlockCrypt interface {
// Encrypt encrypts the whole block in src into dst.
// Dst and src may point at the same memory.
Encrypt(dst, src []byte)
// Decrypt decrypts the whole block in src into dst.
// Dst and src may point at the same memory.
Decrypt(dst, src []byte)
}
var _ BlockCrypt = &aeadCrypt{}
// aeadCrypt implements BlockCrypt interface using cipher.AEAD
type aeadCrypt struct {
aead cipher.AEAD
}
func (aeadCrypt) Encrypt(_, _ []byte) {
panic("called Encrypt on AEAD crypt")
}
func (aeadCrypt) Decrypt(_, _ []byte) {
panic("called Decrypt on AEAD crypt")
}
func (a *aeadCrypt) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
if dst == nil || cap(dst)-len(dst) < len(plaintext)+a.aead.Overhead() {
panic("AEAD Seal allocated new slice, please increase MTU size")
}
return a.aead.Seal(dst, nonce, plaintext, additionalData)
}
func (a *aeadCrypt) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
return a.aead.Open(dst, nonce, ciphertext, additionalData)
}
func (a *aeadCrypt) NonceSize() int {
return a.aead.NonceSize()
}
func (a *aeadCrypt) Overhead() int {
return a.aead.Overhead()
}
// NewAEADCrypt creates an AEAD BlockCrypt instance from an existing cipher.AEAD
func NewAEADCrypt(aead cipher.AEAD) BlockCrypt {
if aead == nil {
return nil
}
return &aeadCrypt{aead}
}
// NewAESGCMCrypt creates an AEAD BlockCrypt instance using AES-GCM
// key must be either 16, 24, or 32 bytes to select
// AES-128, AES-192, or AES-256.
func NewAESGCMCrypt(key []byte) (BlockCrypt, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
return &aeadCrypt{aesgcm}, nil
}
var _ BlockCrypt = &blockCrypt{}
// blockCrypt implements BlockCrypt interface using a cipher.Block
type blockCrypt struct {
encMu sync.Mutex
decMu sync.Mutex
encbuf []byte // encryption working buffer
decbuf []byte // decryption working buffer
block cipher.Block
blockSize int // cached block size
}
//go:nosplit
func (c *blockCrypt) Encrypt(dst, src []byte) {
c.encMu.Lock()
encrypt(c.block, dst, src, c.encbuf)
c.encMu.Unlock()
}
//go:nosplit
func (c *blockCrypt) Decrypt(dst, src []byte) {
c.decMu.Lock()
decrypt(c.block, dst, src, c.decbuf)
c.decMu.Unlock()
}
func newBlockCrypt(block cipher.Block) BlockCrypt {
blockSize := block.BlockSize()
return &blockCrypt{
block: block,
blockSize: blockSize,
encbuf: make([]byte, blockSize),
decbuf: make([]byte, 2*blockSize),
}
}
type salsa20BlockCrypt struct {
key [32]byte
}
// NewSalsa20BlockCrypt https://en.wikipedia.org/wiki/Salsa20
func NewSalsa20BlockCrypt(key []byte) (BlockCrypt, error) {
c := new(salsa20BlockCrypt)
copy(c.key[:], key)
return c, nil
}
//go:nosplit
func (c *salsa20BlockCrypt) Encrypt(dst, src []byte) {
if len(src) < 8 {
return
}
salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
if &dst[0] != &src[0] {
copy(dst[:8], src[:8])
}
}
//go:nosplit
func (c *salsa20BlockCrypt) Decrypt(dst, src []byte) {
if len(src) < 8 {
return
}
salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
if &dst[0] != &src[0] {
copy(dst[:8], src[:8])
}
}
// NewSM4BlockCrypt https://github.com/tjfoc/gmsm/tree/master/sm4
func NewSM4BlockCrypt(key []byte) (BlockCrypt, error) {
block, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}
return newBlockCrypt(block), nil
}
// NewTwofishBlockCrypt https://en.wikipedia.org/wiki/Twofish
func NewTwofishBlockCrypt(key []byte) (BlockCrypt, error) {
block, err := twofish.NewCipher(key)
if err != nil {
return nil, err
}
return newBlockCrypt(block), nil
}
// NewTripleDESBlockCrypt https://en.wikipedia.org/wiki/Triple_DES
func NewTripleDESBlockCrypt(key []byte) (BlockCrypt, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
return newBlockCrypt(block), nil
}
// NewCast5BlockCrypt https://en.wikipedia.org/wiki/CAST-128
func NewCast5BlockCrypt(key []byte) (BlockCrypt, error) {
block, err := cast5.NewCipher(key)
if err != nil {
return nil, err
}
return newBlockCrypt(block), nil
}
// NewBlowfishBlockCrypt https://en.wikipedia.org/wiki/Blowfish_(cipher)
func NewBlowfishBlockCrypt(key []byte) (BlockCrypt, error) {
block, err := blowfish.NewCipher(key)
if err != nil {
return nil, err
}
return newBlockCrypt(block), nil
}
// NewAESBlockCrypt https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
func NewAESBlockCrypt(key []byte) (BlockCrypt, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return newBlockCrypt(block), nil
}
// NewTEABlockCrypt https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
func NewTEABlockCrypt(key []byte) (BlockCrypt, error) {
block, err := tea.NewCipherWithRounds(key, 16)
if err != nil {
return nil, err
}
return newBlockCrypt(block), nil
}
// NewXTEABlockCrypt https://en.wikipedia.org/wiki/XTEA
func NewXTEABlockCrypt(key []byte) (BlockCrypt, error) {
block, err := xtea.NewCipher(key)
if err != nil {
return nil, err
}
return newBlockCrypt(block), nil
}
type simpleXORBlockCrypt struct {
xortbl []byte
}
// NewSimpleXORBlockCrypt simple xor with key expanding
func NewSimpleXORBlockCrypt(key []byte) (BlockCrypt, error) {
c := new(simpleXORBlockCrypt)
c.xortbl = pbkdf2.Key(key, []byte(saltxor), 32, mtuLimit, sha1.New)
return c, nil
}
func (c *simpleXORBlockCrypt) Encrypt(dst, src []byte) {
if len(src) == 0 {
return
}
subtle.XORBytes(dst, src, c.xortbl)
}
func (c *simpleXORBlockCrypt) Decrypt(dst, src []byte) {
if len(src) == 0 {
return
}
subtle.XORBytes(dst, src, c.xortbl)
}
type noneBlockCrypt struct{}
// NewNoneBlockCrypt does nothing but copying
func NewNoneBlockCrypt(key []byte) (BlockCrypt, error) {
return new(noneBlockCrypt), nil
}
//go:nosplit
func (c *noneBlockCrypt) Encrypt(dst, src []byte) {
if len(src) == 0 {
return
}
if &dst[0] != &src[0] {
copy(dst, src)
}
}
//go:nosplit
func (c *noneBlockCrypt) Decrypt(dst, src []byte) {
if len(src) == 0 {
return
}
if &dst[0] != &src[0] {
copy(dst, src)
}
}
// -----------------------------------------------------------------------
// CFB-mode encryption/decryption
// -----------------------------------------------------------------------
//
// For block ciphers (non-AEAD), packets are encrypted using a local variant
// of CFB (Cipher Feedback) mode. The encrypt/decrypt functions below are
// hand-optimized with 8x loop unrolling to reduce data dependencies
// between consecutive block cipher calls.
//
// Two block sizes are supported:
// - 8-byte blocks (e.g. Blowfish, CAST5, DES, TEA, XTEA)
// - 16-byte blocks (e.g. AES, Twofish, SM4)
// encrypt dispatches to the appropriate block-size-specific CFB encryptor.
func encrypt(block cipher.Block, dst, src, buf []byte) {
switch block.BlockSize() {
case 8:
encrypt8(block, dst, src, buf)
case 16:
encrypt16(block, dst, src, buf)
default:
panic("unsupported cipher block size")
}
}
// encrypt8 performs CFB encryption for 8-byte block ciphers.
// Uses 8x loop unrolling for throughput optimization.
func encrypt8(block cipher.Block, dst, src, buf []byte) {
tbl := buf[:8]
block.Encrypt(tbl, initialVector)
n := len(src) >> 3 // number of full 8-byte blocks
base := 0
repeat := n >> 3 // number of 8-block groups (64 bytes each)
left := n & 7 // remaining blocks after groups
for range repeat {
s := src[base:][0:64]
d := dst[base:][0:64]
// 1
subtle.XORBytes(d[0:8], s[0:8], tbl)
block.Encrypt(tbl, d[0:8])
// 2
subtle.XORBytes(d[8:16], s[8:16], tbl)
block.Encrypt(tbl, d[8:16])
// 3
subtle.XORBytes(d[16:24], s[16:24], tbl)
block.Encrypt(tbl, d[16:24])
// 4
subtle.XORBytes(d[24:32], s[24:32], tbl)
block.Encrypt(tbl, d[24:32])
// 5
subtle.XORBytes(d[32:40], s[32:40], tbl)
block.Encrypt(tbl, d[32:40])
// 6
subtle.XORBytes(d[40:48], s[40:48], tbl)
block.Encrypt(tbl, d[40:48])
// 7
subtle.XORBytes(d[48:56], s[48:56], tbl)
block.Encrypt(tbl, d[48:56])
// 8
subtle.XORBytes(d[56:64], s[56:64], tbl)
block.Encrypt(tbl, d[56:64])
base += 64
}
switch left {
case 7:
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
block.Encrypt(tbl, dst[base:base+8])
base += 8
fallthrough
case 6:
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
block.Encrypt(tbl, dst[base:base+8])
base += 8
fallthrough
case 5:
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
block.Encrypt(tbl, dst[base:base+8])
base += 8
fallthrough
case 4:
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
block.Encrypt(tbl, dst[base:base+8])
base += 8
fallthrough
case 3:
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
block.Encrypt(tbl, dst[base:base+8])
base += 8
fallthrough
case 2:
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
block.Encrypt(tbl, dst[base:base+8])
base += 8
fallthrough
case 1:
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
block.Encrypt(tbl, dst[base:base+8])
base += 8
fallthrough
case 0:
subtle.XORBytes(dst[base:], src[base:], tbl)
}
}
// encrypt16 performs CFB encryption for 16-byte block ciphers.
// Uses 8x loop unrolling for throughput optimization.
func encrypt16(block cipher.Block, dst, src, buf []byte) {
tbl := buf[:16]
block.Encrypt(tbl, initialVector)
n := len(src) >> 4 // number of full 16-byte blocks
base := 0
repeat := n >> 3 // number of 8-block groups (128 bytes each)
left := n & 7 // remaining blocks after groups
for range repeat {
s := src[base:][0:128]
d := dst[base:][0:128]
// 1
subtle.XORBytes(d[0:16], s[0:16], tbl)
block.Encrypt(tbl, d[0:16])
// 2
subtle.XORBytes(d[16:32], s[16:32], tbl)
block.Encrypt(tbl, d[16:32])
// 3
subtle.XORBytes(d[32:48], s[32:48], tbl)
block.Encrypt(tbl, d[32:48])
// 4
subtle.XORBytes(d[48:64], s[48:64], tbl)
block.Encrypt(tbl, d[48:64])
// 5
subtle.XORBytes(d[64:80], s[64:80], tbl)
block.Encrypt(tbl, d[64:80])
// 6
subtle.XORBytes(d[80:96], s[80:96], tbl)
block.Encrypt(tbl, d[80:96])
// 7
subtle.XORBytes(d[96:112], s[96:112], tbl)
block.Encrypt(tbl, d[96:112])
// 8
subtle.XORBytes(d[112:128], s[112:128], tbl)
block.Encrypt(tbl, d[112:128])
base += 128
}
switch left {
case 7:
subtle.XORBytes(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 6:
subtle.XORBytes(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 5:
subtle.XORBytes(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 4:
subtle.XORBytes(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 3:
subtle.XORBytes(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 2:
subtle.XORBytes(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 1:
subtle.XORBytes(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 0:
subtle.XORBytes(dst[base:], src[base:], tbl)
}
}
// decrypt dispatches to the appropriate block-size-specific CFB decryptor.
func decrypt(block cipher.Block, dst, src, buf []byte) {
switch block.BlockSize() {
case 8:
decrypt8(block, dst, src, buf)
case 16:
decrypt16(block, dst, src, buf)
default:
panic("unsupported cipher block size")
}
}
// decrypt8 performs CFB decryption for 8-byte block ciphers.
// Uses double-buffering (tbl/next) with 8x loop unrolling
// to break the data dependency chain between consecutive blocks.
func decrypt8(block cipher.Block, dst, src, buf []byte) {
tbl := buf[0:8]
next := buf[8:16]
block.Encrypt(tbl, initialVector)
n := len(src) >> 3 // number of full 8-byte blocks
base := 0
repeat := n >> 3 // number of 8-block groups (64 bytes each)
left := n & 7 // remaining blocks after groups
// 8x loop unrolling: alternates tbl/next to relieve data dependency
for range repeat {
s := src[base:][0:64]
d := dst[base:][0:64]
// 1
block.Encrypt(next, s[0:8])
subtle.XORBytes(d[0:8], s[0:8], tbl)
// 2
block.Encrypt(tbl, s[8:16])
subtle.XORBytes(d[8:16], s[8:16], next)
// 3
block.Encrypt(next, s[16:24])
subtle.XORBytes(d[16:24], s[16:24], tbl)
// 4
block.Encrypt(tbl, s[24:32])
subtle.XORBytes(d[24:32], s[24:32], next)
// 5
block.Encrypt(next, s[32:40])
subtle.XORBytes(d[32:40], s[32:40], tbl)
// 6
block.Encrypt(tbl, s[40:48])
subtle.XORBytes(d[40:48], s[40:48], next)
// 7
block.Encrypt(next, s[48:56])
subtle.XORBytes(d[48:56], s[48:56], tbl)
// 8
block.Encrypt(tbl, s[56:64])
subtle.XORBytes(d[56:64], s[56:64], next)
base += 64
}
switch left {
case 7:
block.Encrypt(next, src[base:base+8])
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
tbl, next = next, tbl
base += 8
fallthrough
case 6:
block.Encrypt(next, src[base:base+8])
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
tbl, next = next, tbl
base += 8
fallthrough
case 5:
block.Encrypt(next, src[base:base+8])
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
tbl, next = next, tbl
base += 8
fallthrough
case 4:
block.Encrypt(next, src[base:base+8])
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
tbl, next = next, tbl
base += 8
fallthrough
case 3:
block.Encrypt(next, src[base:base+8])
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
tbl, next = next, tbl
base += 8
fallthrough
case 2:
block.Encrypt(next, src[base:base+8])
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
tbl, next = next, tbl
base += 8
fallthrough
case 1:
block.Encrypt(next, src[base:base+8])
subtle.XORBytes(dst[base:base+8], src[base:base+8], tbl)
tbl, next = next, tbl
base += 8
fallthrough
case 0:
subtle.XORBytes(dst[base:], src[base:], tbl)
}
}
// decrypt16 performs CFB decryption for 16-byte block ciphers.
// Uses double-buffering (tbl/next) with 8x loop unrolling.
func decrypt16(block cipher.Block, dst, src, buf []byte) {
tbl := buf[0:16]
next := buf[16:32]
block.Encrypt(tbl, initialVector)
n := len(src) >> 4 // number of full 16-byte blocks
base := 0
repeat := n >> 3 // number of 8-block groups (128 bytes each)
left := n & 7 // remaining blocks after groups
// 8x loop unrolling: alternates tbl/next to relieve data dependency
for range repeat {
s := src[base:][0:128]
d := dst[base:][0:128]
// 1
block.Encrypt(next, s[0:16])
subtle.XORBytes(d[0:16], s[0:16], tbl)
// 2
block.Encrypt(tbl, s[16:32])
subtle.XORBytes(d[16:32], s[16:32], next)
// 3
block.Encrypt(next, s[32:48])
subtle.XORBytes(d[32:48], s[32:48], tbl)
// 4
block.Encrypt(tbl, s[48:64])
subtle.XORBytes(d[48:64], s[48:64], next)
// 5
block.Encrypt(next, s[64:80])
subtle.XORBytes(d[64:80], s[64:80], tbl)
// 6
block.Encrypt(tbl, s[80:96])
subtle.XORBytes(d[80:96], s[80:96], next)
// 7
block.Encrypt(next, s[96:112])
subtle.XORBytes(d[96:112], s[96:112], tbl)
// 8
block.Encrypt(tbl, s[112:128])
subtle.XORBytes(d[112:128], s[112:128], next)
base += 128
}
switch left {
case 7:
block.Encrypt(next, src[base:])
subtle.XORBytes(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 6:
block.Encrypt(next, src[base:])
subtle.XORBytes(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 5:
block.Encrypt(next, src[base:])
subtle.XORBytes(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 4:
block.Encrypt(next, src[base:])
subtle.XORBytes(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 3:
block.Encrypt(next, src[base:])
subtle.XORBytes(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 2:
block.Encrypt(next, src[base:])
subtle.XORBytes(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 1:
block.Encrypt(next, src[base:])
subtle.XORBytes(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 0:
subtle.XORBytes(dst[base:], src[base:], tbl)
}
}