171 lines
3.9 KiB
Go
171 lines
3.9 KiB
Go
package udp
|
|
|
|
import (
|
|
"crypto/rc4"
|
|
"errors"
|
|
)
|
|
|
|
// EQ2EMu CRC32 polynomial (reversed)
|
|
const crcPolynomial = 0xEDB88320
|
|
|
|
// Pre-computed CRC32 lookup table for fast calculation
|
|
var crcTable [256]uint32
|
|
|
|
// init builds the CRC lookup table at package initialization
|
|
func init() {
|
|
for i := range crcTable {
|
|
crc := uint32(i)
|
|
for range 8 {
|
|
if crc&1 == 1 {
|
|
crc = (crc >> 1) ^ crcPolynomial
|
|
} else {
|
|
crc >>= 1
|
|
}
|
|
}
|
|
crcTable[i] = crc
|
|
}
|
|
}
|
|
|
|
// CalculateCRC32 computes CRC32 using EQ2EMu's algorithm
|
|
// Returns 16-bit value by truncating the upper bits
|
|
func CalculateCRC32(data []byte) uint16 {
|
|
crc := uint32(0xFFFFFFFF)
|
|
|
|
// Use lookup table for efficient calculation
|
|
for _, b := range data {
|
|
crc = crcTable[byte(crc)^b] ^ (crc >> 8)
|
|
}
|
|
|
|
// Return inverted result truncated to 16 bits
|
|
return uint16(^crc)
|
|
}
|
|
|
|
// ValidateCRC checks if packet has valid CRC
|
|
// Expects CRC to be the last 2 bytes of data
|
|
func ValidateCRC(data []byte) bool {
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
|
|
// Split payload and CRC
|
|
payload := data[:len(data)-2]
|
|
expectedCRC := uint16(data[len(data)-2]) | (uint16(data[len(data)-1]) << 8)
|
|
|
|
// Calculate and compare
|
|
actualCRC := CalculateCRC32(payload)
|
|
return expectedCRC == actualCRC
|
|
}
|
|
|
|
// AppendCRC adds 16-bit CRC to the end of data
|
|
func AppendCRC(data []byte) []byte {
|
|
crc := CalculateCRC32(data)
|
|
result := make([]byte, len(data)+2)
|
|
copy(result, data)
|
|
|
|
// Append CRC in little-endian format
|
|
result[len(data)] = byte(crc)
|
|
result[len(data)+1] = byte(crc >> 8)
|
|
|
|
return result
|
|
}
|
|
|
|
// ValidateAndStrip validates CRC and returns data without CRC suffix
|
|
func ValidateAndStrip(data []byte) ([]byte, bool) {
|
|
if !ValidateCRC(data) {
|
|
return nil, false
|
|
}
|
|
return data[:len(data)-2], true
|
|
}
|
|
|
|
// Crypto handles RC4 encryption/decryption for EQ2EMu protocol
|
|
type Crypto struct {
|
|
clientCipher *rc4.Cipher // Cipher for decrypting client data
|
|
serverCipher *rc4.Cipher // Cipher for encrypting server data
|
|
key []byte // Encryption key
|
|
encrypted bool // Whether encryption is active
|
|
}
|
|
|
|
// NewCrypto creates a new crypto instance with encryption disabled
|
|
func NewCrypto() *Crypto {
|
|
return &Crypto{}
|
|
}
|
|
|
|
// SetKey initializes RC4 encryption with the given key
|
|
// Creates separate ciphers for client and server with 20-byte priming
|
|
func (c *Crypto) SetKey(key []byte) error {
|
|
if len(key) == 0 {
|
|
return errors.New("encryption key cannot be empty")
|
|
}
|
|
|
|
// Create separate RC4 ciphers for bidirectional communication
|
|
clientCipher, err := rc4.NewCipher(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
serverCipher, err := rc4.NewCipher(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Prime both ciphers with 20 dummy bytes per EQ2EMu protocol
|
|
dummy := make([]byte, 20)
|
|
clientCipher.XORKeyStream(dummy, dummy)
|
|
serverCipher.XORKeyStream(dummy, dummy)
|
|
|
|
c.clientCipher = clientCipher
|
|
c.serverCipher = serverCipher
|
|
c.key = make([]byte, len(key))
|
|
copy(c.key, key)
|
|
c.encrypted = true
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsEncrypted returns whether encryption is currently active
|
|
func (c *Crypto) IsEncrypted() bool {
|
|
return c.encrypted
|
|
}
|
|
|
|
// Encrypt encrypts data for transmission to client
|
|
func (c *Crypto) Encrypt(data []byte) []byte {
|
|
if !c.encrypted || c.serverCipher == nil {
|
|
return data
|
|
}
|
|
|
|
encrypted := make([]byte, len(data))
|
|
copy(encrypted, data)
|
|
c.serverCipher.XORKeyStream(encrypted, encrypted)
|
|
return encrypted
|
|
}
|
|
|
|
// Decrypt decrypts data received from client
|
|
func (c *Crypto) Decrypt(data []byte) []byte {
|
|
if !c.encrypted || c.clientCipher == nil {
|
|
return data
|
|
}
|
|
|
|
decrypted := make([]byte, len(data))
|
|
copy(decrypted, data)
|
|
c.clientCipher.XORKeyStream(decrypted, decrypted)
|
|
return decrypted
|
|
}
|
|
|
|
// GetKey returns a copy of the encryption key
|
|
func (c *Crypto) GetKey() []byte {
|
|
if c.key == nil {
|
|
return nil
|
|
}
|
|
keyCopy := make([]byte, len(c.key))
|
|
copy(keyCopy, c.key)
|
|
return keyCopy
|
|
}
|
|
|
|
// Reset disables encryption and clears keys
|
|
func (c *Crypto) Reset() {
|
|
c.clientCipher = nil
|
|
c.serverCipher = nil
|
|
c.key = nil
|
|
c.encrypted = false
|
|
}
|