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 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 // Serialize writes the protocol packet to a destination buffer
func (p *ProtoPacket) Serialize(dest []byte, offset int8) uint32 { func (p *ProtoPacket) Serialize(dest []byte, offset int8) uint32 {
pos := 0 pos := 0

View File

@ -328,9 +328,32 @@ func (s *Stream) processQueues() error {
s.resendQueue = s.resendQueue[1:] s.resendQueue = s.resendQueue[1:]
if time.Now().After(pending.nextRetry) { 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) 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.attempts++
pending.sentTime = time.Now() pending.sentTime = time.Now()
@ -344,7 +367,7 @@ func (s *Stream) processQueues() error {
s.mu.Unlock() s.mu.Unlock()
atomic.AddUint64(&s.retransmits, 1) atomic.AddUint64(&s.retransmits, 1)
s.sendRaw(opcodes.OP_Packet, data) s.sendRawWithCRC(opcodes.OP_Packet, data)
s.mu.Lock() s.mu.Lock()
} else { } else {
s.resendQueue = append(s.resendQueue, pending) s.resendQueue = append(s.resendQueue, pending)
@ -369,11 +392,32 @@ func (s *Stream) processQueues() error {
proto := s.unreliableQueue[0] proto := s.unreliableQueue[0]
s.unreliableQueue = s.unreliableQueue[1:] s.unreliableQueue = s.unreliableQueue[1:]
data := make([]byte, proto.Size()) // Prepare unreliable packets too
proto.Serialize(data, 0) 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.mu.Unlock()
s.sendRaw(proto.Opcode, data) s.sendRawWithCRC(proto.Opcode, data)
s.mu.Lock() s.mu.Lock()
} }
@ -386,12 +430,27 @@ func (s *Stream) processQueues() error {
// sendFragmented sends a fragmented packet (matches EQStream::SendPacket) // sendFragmented sends a fragmented packet (matches EQStream::SendPacket)
func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error { func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
// Prepare packet first // Prepare packet first
offset := proto.PreparePacket(int16(s.maxLen)) encryptOffset := proto.PreparePacket(int16(s.maxLen))
if offset < 0 { if encryptOffset < 0 {
return fmt.Errorf("failed to prepare packet") return fmt.Errorf("failed to prepare packet")
} }
// Encrypt the prepared packet data BEFORE fragmentation
data := proto.Buffer 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)) length := uint32(len(data))
// First fragment with total length // First fragment with total length
@ -403,7 +462,8 @@ func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
used := copy(out.Buffer[4:], data) used := copy(out.Buffer[4:], data)
out.Buffer = out.Buffer[:4+used] 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 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) 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 return err
} }
@ -426,16 +486,17 @@ func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
return nil return nil
} }
// sendReliable sends a packet reliably with sequencing // sendReliableFragment sends a fragment packet (already encrypted data)
func (s *Stream) sendReliable(proto *packets.ProtoPacket) error { func (s *Stream) sendReliableFragment(proto *packets.ProtoPacket) error {
s.mu.Lock() s.mu.Lock()
seq := s.seqOut seq := s.seqOut
s.seqOut = s.incrementSequence(s.seqOut) s.seqOut = s.incrementSequence(s.seqOut)
s.mu.Unlock() 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) binary.BigEndian.PutUint16(data[:2], seq)
proto.Serialize(data[2:], 0) copy(data[2:], proto.Buffer)
pending := &pendingPacket{ pending := &pendingPacket{
packet: proto.Copy(), packet: proto.Copy(),
@ -449,7 +510,59 @@ func (s *Stream) sendReliable(proto *packets.ProtoPacket) error {
s.pendingAcks[seq] = pending s.pendingAcks[seq] = pending
s.mu.Unlock() 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 // Helper methods
@ -500,6 +613,7 @@ func (s *Stream) handleSessionRequest(data []byte) error {
binary.BigEndian.PutUint32(response[11:15], version) binary.BigEndian.PutUint32(response[11:15], version)
s.state.Store(StateEstablished) s.state.Store(StateEstablished)
// Session packets don't have CRC or encryption (matches C++)
return s.sendRaw(opcodes.OP_SessionResponse, response) return s.sendRaw(opcodes.OP_SessionResponse, response)
} }
@ -860,7 +974,7 @@ func (s *Stream) sendPendingAcks() {
for _, seq := range s.ackQueue { for _, seq := range s.ackQueue {
data := make([]byte, 2) data := make([]byte, 2)
binary.BigEndian.PutUint16(data, seq) binary.BigEndian.PutUint16(data, seq)
s.sendRaw(opcodes.OP_Ack, data) s.sendRawWithCRC(opcodes.OP_Ack, data)
} }
s.ackQueue = s.ackQueue[:0] s.ackQueue = s.ackQueue[:0]
@ -871,18 +985,18 @@ func (s *Stream) sendPendingAcks() {
func (s *Stream) sendAckImmediate(seq uint16) error { func (s *Stream) sendAckImmediate(seq uint16) error {
data := make([]byte, 2) data := make([]byte, 2)
binary.BigEndian.PutUint16(data, seq) 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 { func (s *Stream) sendOutOfOrderAck(seq uint16) error {
data := make([]byte, 2) data := make([]byte, 2)
binary.BigEndian.PutUint16(data, seq) binary.BigEndian.PutUint16(data, seq)
return s.sendRaw(opcodes.OP_OutOfOrderAck, data) return s.sendRawWithCRC(opcodes.OP_OutOfOrderAck, data)
} }
func (s *Stream) sendKeepalive() { func (s *Stream) sendKeepalive() {
if s.state.Load() == StateEstablished { if s.state.Load() == StateEstablished {
s.sendRaw(opcodes.OP_KeepAlive, nil) s.sendRawWithCRC(opcodes.OP_KeepAlive, nil)
} }
if s.keepAliveTimer != nil { if s.keepAliveTimer != nil {
s.keepAliveTimer.Reset(10 * time.Second) s.keepAliveTimer.Reset(10 * time.Second)
@ -902,20 +1016,27 @@ 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 { func (s *Stream) sendRaw(opcode uint16, data []byte) error {
packet := make([]byte, 2+len(data)) packet := make([]byte, 2+len(data))
binary.BigEndian.PutUint16(packet[:2], opcode) binary.BigEndian.PutUint16(packet[:2], opcode)
copy(packet[2:], data) copy(packet[2:], data)
// Add CRC16 atomic.AddUint64(&s.packetsOut, 1)
packet = packets.AppendCRC(packet, s.crcKey) atomic.AddUint64(&s.bytesOut, uint64(len(packet)))
// Encrypt if needed return s.conn.AsyncWrite(packet, nil)
if s.cipher != nil {
s.cipher.Encrypt(packet)
} }
// 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.packetsOut, 1)
atomic.AddUint64(&s.bytesOut, uint64(len(packet))) atomic.AddUint64(&s.bytesOut, uint64(len(packet)))
@ -946,6 +1067,7 @@ func (s *Stream) SendSessionRequest() error {
binary.BigEndian.PutUint16(data[8:10], s.maxLen) binary.BigEndian.PutUint16(data[8:10], s.maxLen)
s.state.Store(StateConnecting) s.state.Store(StateConnecting)
// Session packets don't have CRC or encryption (matches C++)
return s.sendRaw(opcodes.OP_SessionRequest, data) return s.sendRaw(opcodes.OP_SessionRequest, data)
} }
@ -974,7 +1096,7 @@ func (s *Stream) Close() error {
} }
s.state.Store(StateClosed) s.state.Store(StateClosed)
s.sendRaw(opcodes.OP_SessionDisconnect, nil) s.sendRawWithCRC(opcodes.OP_SessionDisconnect, nil)
// Stop all timers // Stop all timers
if s.keepAliveTimer != nil { if s.keepAliveTimer != nil {