1
0

fix encryption offsets

This commit is contained in:
Sky Johnson 2025-09-03 14:09:30 -05:00
parent d70daa98be
commit 50d4d9d6f0
2 changed files with 154 additions and 27 deletions

View File

@ -160,6 +160,11 @@ func (p *ProtoPacket) PreparePacket(maxLen int16) int8 {
return offset
}
// IsCompressed returns whether the packet is compressed
func (p *ProtoPacket) IsCompressed() bool {
return p.eq2Compressed
}
// Serialize writes the protocol packet to a destination buffer
func (p *ProtoPacket) Serialize(dest []byte, offset int8) uint32 {
pos := 0

View File

@ -328,9 +328,32 @@ func (s *Stream) processQueues() error {
s.resendQueue = s.resendQueue[1:]
if time.Now().After(pending.nextRetry) {
data := make([]byte, pending.packet.Size()+2)
// Resend already prepared packet
proto := pending.packet
var encryptOffset int8
if proto.LoginOp != opcodes.OP_Unknown {
encryptOffset = proto.PreparePacket(int16(s.maxLen))
}
data := make([]byte, len(proto.Buffer)+2)
binary.BigEndian.PutUint16(data[:2], pending.seq)
pending.packet.Serialize(data[2:], 0)
copy(data[2:], proto.Buffer)
// Apply encryption if needed (BEFORE CRC, with proper offset)
if s.cipher != nil && len(data) > 2 {
if proto.IsCompressed() {
// Compressed packet: encrypt from offset 3
if len(data) > 3 {
s.cipher.Encrypt(data[3:])
}
} else {
// Uncompressed packet: encrypt from 2 + offset
offset := 2 + int(encryptOffset)
if len(data) > offset {
s.cipher.Encrypt(data[offset:])
}
}
}
pending.attempts++
pending.sentTime = time.Now()
@ -344,7 +367,7 @@ func (s *Stream) processQueues() error {
s.mu.Unlock()
atomic.AddUint64(&s.retransmits, 1)
s.sendRaw(opcodes.OP_Packet, data)
s.sendRawWithCRC(opcodes.OP_Packet, data)
s.mu.Lock()
} else {
s.resendQueue = append(s.resendQueue, pending)
@ -369,11 +392,32 @@ func (s *Stream) processQueues() error {
proto := s.unreliableQueue[0]
s.unreliableQueue = s.unreliableQueue[1:]
data := make([]byte, proto.Size())
proto.Serialize(data, 0)
// Prepare unreliable packets too
var encryptOffset int8
if proto.LoginOp != opcodes.OP_Unknown {
encryptOffset = proto.PreparePacket(int16(s.maxLen))
}
data := make([]byte, len(proto.Buffer))
copy(data, proto.Buffer)
// Apply encryption if needed (BEFORE CRC)
if s.cipher != nil && len(data) > 0 {
if proto.IsCompressed() {
// Compressed: encrypt from offset 1 (skip compress flag)
if len(data) > 1 {
s.cipher.Encrypt(data[1:])
}
} else if encryptOffset > 0 {
// Uncompressed: encrypt from offset
if len(data) > int(encryptOffset) {
s.cipher.Encrypt(data[encryptOffset:])
}
}
}
s.mu.Unlock()
s.sendRaw(proto.Opcode, data)
s.sendRawWithCRC(proto.Opcode, data)
s.mu.Lock()
}
@ -386,12 +430,27 @@ func (s *Stream) processQueues() error {
// sendFragmented sends a fragmented packet (matches EQStream::SendPacket)
func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
// Prepare packet first
offset := proto.PreparePacket(int16(s.maxLen))
if offset < 0 {
encryptOffset := proto.PreparePacket(int16(s.maxLen))
if encryptOffset < 0 {
return fmt.Errorf("failed to prepare packet")
}
// Encrypt the prepared packet data BEFORE fragmentation
data := proto.Buffer
if s.cipher != nil && len(data) > 0 {
if proto.IsCompressed() {
// Compressed: encrypt from offset 1 (skip compress flag)
if len(data) > 1 {
s.cipher.Encrypt(data[1:])
}
} else if encryptOffset > 0 {
// Uncompressed: encrypt from offset
if len(data) > int(encryptOffset) {
s.cipher.Encrypt(data[encryptOffset:])
}
}
}
length := uint32(len(data))
// First fragment with total length
@ -403,7 +462,8 @@ func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
used := copy(out.Buffer[4:], data)
out.Buffer = out.Buffer[:4+used]
if err := s.sendReliable(out); err != nil {
// Fragment packets are sent without additional encryption
if err := s.sendReliableFragment(out); err != nil {
return err
}
@ -416,7 +476,7 @@ func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
}
frag := packets.NewProtoPacket(opcodes.OP_Fragment, data[pos:pos+chunkSize], s.opcodeManager)
if err := s.sendReliable(frag); err != nil {
if err := s.sendReliableFragment(frag); err != nil {
return err
}
@ -426,16 +486,17 @@ func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
return nil
}
// sendReliable sends a packet reliably with sequencing
func (s *Stream) sendReliable(proto *packets.ProtoPacket) error {
// sendReliableFragment sends a fragment packet (already encrypted data)
func (s *Stream) sendReliableFragment(proto *packets.ProtoPacket) error {
s.mu.Lock()
seq := s.seqOut
s.seqOut = s.incrementSequence(s.seqOut)
s.mu.Unlock()
data := make([]byte, proto.Size()+2)
// Build packet with sequence (data is already encrypted)
data := make([]byte, len(proto.Buffer)+2)
binary.BigEndian.PutUint16(data[:2], seq)
proto.Serialize(data[2:], 0)
copy(data[2:], proto.Buffer)
pending := &pendingPacket{
packet: proto.Copy(),
@ -449,7 +510,59 @@ func (s *Stream) sendReliable(proto *packets.ProtoPacket) error {
s.pendingAcks[seq] = pending
s.mu.Unlock()
return s.sendRaw(opcodes.OP_Packet, data)
return s.sendRawWithCRC(opcodes.OP_Packet, data)
}
// sendReliable sends a packet reliably with sequencing
func (s *Stream) sendReliable(proto *packets.ProtoPacket) error {
s.mu.Lock()
seq := s.seqOut
s.seqOut = s.incrementSequence(s.seqOut)
s.mu.Unlock()
// Prepare packet first (adds headers, compression)
var encryptOffset int8
if proto.LoginOp != opcodes.OP_Unknown {
encryptOffset = proto.PreparePacket(int16(s.maxLen))
if encryptOffset < 0 {
return fmt.Errorf("failed to prepare packet")
}
}
// Build packet with sequence
data := make([]byte, len(proto.Buffer)+2)
binary.BigEndian.PutUint16(data[:2], seq)
copy(data[2:], proto.Buffer)
// Apply encryption if needed (BEFORE CRC, with proper offset)
if s.cipher != nil && len(data) > 2 {
if proto.IsCompressed() {
// Compressed packet: encrypt from compress offset (typically 3)
if len(data) > 3 {
s.cipher.Encrypt(data[3:]) // Skip seq[2] + compress_flag[1]
}
} else {
// Uncompressed packet: encrypt from 2 + offset
offset := 2 + int(encryptOffset)
if len(data) > offset {
s.cipher.Encrypt(data[offset:])
}
}
}
pending := &pendingPacket{
packet: proto.Copy(),
seq: seq,
sentTime: time.Now(),
attempts: 1,
nextRetry: time.Now().Add(s.rto),
}
s.mu.Lock()
s.pendingAcks[seq] = pending
s.mu.Unlock()
return s.sendRawWithCRC(opcodes.OP_Packet, data)
}
// Helper methods
@ -500,6 +613,7 @@ func (s *Stream) handleSessionRequest(data []byte) error {
binary.BigEndian.PutUint32(response[11:15], version)
s.state.Store(StateEstablished)
// Session packets don't have CRC or encryption (matches C++)
return s.sendRaw(opcodes.OP_SessionResponse, response)
}
@ -860,7 +974,7 @@ func (s *Stream) sendPendingAcks() {
for _, seq := range s.ackQueue {
data := make([]byte, 2)
binary.BigEndian.PutUint16(data, seq)
s.sendRaw(opcodes.OP_Ack, data)
s.sendRawWithCRC(opcodes.OP_Ack, data)
}
s.ackQueue = s.ackQueue[:0]
@ -871,18 +985,18 @@ func (s *Stream) sendPendingAcks() {
func (s *Stream) sendAckImmediate(seq uint16) error {
data := make([]byte, 2)
binary.BigEndian.PutUint16(data, seq)
return s.sendRaw(opcodes.OP_Ack, data)
return s.sendRawWithCRC(opcodes.OP_Ack, data)
}
func (s *Stream) sendOutOfOrderAck(seq uint16) error {
data := make([]byte, 2)
binary.BigEndian.PutUint16(data, seq)
return s.sendRaw(opcodes.OP_OutOfOrderAck, data)
return s.sendRawWithCRC(opcodes.OP_OutOfOrderAck, data)
}
func (s *Stream) sendKeepalive() {
if s.state.Load() == StateEstablished {
s.sendRaw(opcodes.OP_KeepAlive, nil)
s.sendRawWithCRC(opcodes.OP_KeepAlive, nil)
}
if s.keepAliveTimer != nil {
s.keepAliveTimer.Reset(10 * time.Second)
@ -902,19 +1016,26 @@ func (s *Stream) handleTimeout() {
}
}
// sendRaw sends raw packet with CRC16
// sendRaw sends raw packet WITHOUT encryption or CRC (for special packets)
func (s *Stream) sendRaw(opcode uint16, data []byte) error {
packet := make([]byte, 2+len(data))
binary.BigEndian.PutUint16(packet[:2], opcode)
copy(packet[2:], data)
// Add CRC16
packet = packets.AppendCRC(packet, s.crcKey)
atomic.AddUint64(&s.packetsOut, 1)
atomic.AddUint64(&s.bytesOut, uint64(len(packet)))
// Encrypt if needed
if s.cipher != nil {
s.cipher.Encrypt(packet)
}
return s.conn.AsyncWrite(packet, nil)
}
// sendRawWithCRC sends raw packet with CRC16 (matches C++ WritePacket)
func (s *Stream) sendRawWithCRC(opcode uint16, data []byte) error {
packet := make([]byte, 2+len(data))
binary.BigEndian.PutUint16(packet[:2], opcode)
copy(packet[2:], data)
// Add CRC16 AFTER encryption (matches C++ order)
packet = packets.AppendCRC(packet, s.crcKey)
atomic.AddUint64(&s.packetsOut, 1)
atomic.AddUint64(&s.bytesOut, uint64(len(packet)))
@ -946,6 +1067,7 @@ func (s *Stream) SendSessionRequest() error {
binary.BigEndian.PutUint16(data[8:10], s.maxLen)
s.state.Store(StateConnecting)
// Session packets don't have CRC or encryption (matches C++)
return s.sendRaw(opcodes.OP_SessionRequest, data)
}
@ -974,7 +1096,7 @@ func (s *Stream) Close() error {
}
s.state.Store(StateClosed)
s.sendRaw(opcodes.OP_SessionDisconnect, nil)
s.sendRawWithCRC(opcodes.OP_SessionDisconnect, nil)
// Stop all timers
if s.keepAliveTimer != nil {