mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-12 15:04:38 +01:00
126 lines
3.5 KiB
Go
126 lines
3.5 KiB
Go
package network
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
|
|
"erupe-ce/network/crypto"
|
|
)
|
|
|
|
// CryptConn represents a MHF encrypted two-way connection,
|
|
// it automatically handles encryption, decryption, and key rotation via it's methods.
|
|
type CryptConn struct {
|
|
conn net.Conn
|
|
readKeyRot uint32
|
|
sendKeyRot uint32
|
|
sentPackets int32
|
|
prevRecvPacketCombinedCheck uint16
|
|
prevSendPacketCombinedCheck uint16
|
|
}
|
|
|
|
// NewCryptConn creates a new CryptConn with proper default values.
|
|
func NewCryptConn(conn net.Conn) *CryptConn {
|
|
cc := &CryptConn{
|
|
conn: conn,
|
|
readKeyRot: 995117,
|
|
sendKeyRot: 995117,
|
|
}
|
|
return cc
|
|
}
|
|
|
|
// ReadPacket reads an packet from the connection and returns the decrypted data.
|
|
func (cc *CryptConn) ReadPacket() ([]byte, error) {
|
|
|
|
// Read the raw 14 byte header.
|
|
headerData := make([]byte, CryptPacketHeaderLength)
|
|
_, err := io.ReadFull(cc.conn, headerData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse the data into a usable struct.
|
|
cph, err := NewCryptPacketHeader(headerData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Now read the encrypted packet body after getting its size from the header.
|
|
encryptedPacketBody := make([]byte, cph.DataSize)
|
|
_, err = io.ReadFull(cc.conn, encryptedPacketBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Update the key rotation before decrypting.
|
|
if cph.KeyRotDelta != 0 {
|
|
cc.readKeyRot = (uint32(cph.KeyRotDelta) * (cc.readKeyRot + 1))
|
|
}
|
|
|
|
out, combinedCheck, check0, check1, check2 := crypto.Decrypt(encryptedPacketBody, cc.readKeyRot, nil)
|
|
if cph.Check0 != check0 || cph.Check1 != check1 || cph.Check2 != check2 {
|
|
fmt.Printf("got c0 %X, c1 %X, c2 %X\n", check0, check1, check2)
|
|
fmt.Printf("want c0 %X, c1 %X, c2 %X\n", cph.Check0, cph.Check1, cph.Check2)
|
|
fmt.Printf("headerData:\n%s\n", hex.Dump(headerData))
|
|
fmt.Printf("encryptedPacketBody:\n%s\n", hex.Dump(encryptedPacketBody))
|
|
|
|
// Attempt to bruteforce it.
|
|
fmt.Println("Crypto out of sync? Attempting bruteforce")
|
|
for key := byte(0); key < 255; key++ {
|
|
out, combinedCheck, check0, check1, check2 = crypto.Decrypt(encryptedPacketBody, 0, &key)
|
|
//fmt.Printf("Key: 0x%X\n%s\n", key, hex.Dump(out))
|
|
if cph.Check0 == check0 && cph.Check1 == check1 && cph.Check2 == check2 {
|
|
fmt.Printf("Bruceforce successful, override key: 0x%X\n", key)
|
|
|
|
// Try to fix key for subsequent packets?
|
|
//cc.readKeyRot = (uint32(key) << 1) + 999983
|
|
|
|
cc.prevRecvPacketCombinedCheck = combinedCheck
|
|
return out, nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("decrypted data checksum doesn't match header")
|
|
}
|
|
|
|
cc.prevRecvPacketCombinedCheck = combinedCheck
|
|
return out, nil
|
|
}
|
|
|
|
// SendPacket encrypts and sends a packet.
|
|
func (cc *CryptConn) SendPacket(data []byte) error {
|
|
keyRotDelta := byte(3)
|
|
|
|
if keyRotDelta != 0 {
|
|
cc.sendKeyRot = (uint32(keyRotDelta) * (cc.sendKeyRot + 1))
|
|
}
|
|
|
|
// Encrypt the data
|
|
encData, combinedCheck, check0, check1, check2 := crypto.Encrypt(data, cc.sendKeyRot, nil)
|
|
|
|
header := &CryptPacketHeader{}
|
|
header.Pf0 = byte(((uint(len(encData)) >> 12) & 0xF3) | 3)
|
|
header.KeyRotDelta = keyRotDelta
|
|
header.PacketNum = uint16(cc.sentPackets)
|
|
header.DataSize = uint16(len(encData))
|
|
header.PrevPacketCombinedCheck = cc.prevSendPacketCombinedCheck
|
|
header.Check0 = check0
|
|
header.Check1 = check1
|
|
header.Check2 = check2
|
|
|
|
headerBytes, err := header.Encode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cc.conn.Write(headerBytes)
|
|
cc.conn.Write(encData)
|
|
|
|
cc.sentPackets++
|
|
cc.prevSendPacketCombinedCheck = combinedCheck
|
|
|
|
return nil
|
|
}
|