From 50d4d9d6f001194332675d2fe20a05d8d534779d Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Wed, 3 Sep 2025 14:09:30 -0500 Subject: [PATCH] fix encryption offsets --- packets/protopacket.go | 5 ++ stream/stream.go | 176 ++++++++++++++++++++++++++++++++++------- 2 files changed, 154 insertions(+), 27 deletions(-) diff --git a/packets/protopacket.go b/packets/protopacket.go index 14b2662..7680127 100644 --- a/packets/protopacket.go +++ b/packets/protopacket.go @@ -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 diff --git a/stream/stream.go b/stream/stream.go index 6287139..ef07715 100644 --- a/stream/stream.go +++ b/stream/stream.go @@ -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 {