mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-12 15:04:38 +01:00
Initial commit
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 The Erupe Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
5
README.md
Normal file
5
README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Erupe
|
||||
|
||||
Nothing here yet :)
|
||||
|
||||
Based on the TW version. Requires a local mirror of the original launcher site to be placed in `./www/g6_launcher` until I can RE the launcher and figure out which JS callbacks it requires.
|
||||
9
go.mod
Normal file
9
go.mod
Normal file
@@ -0,0 +1,9 @@
|
||||
module github.com/Andoryuuta/Erupe
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3
|
||||
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
)
|
||||
6
go.sum
Normal file
6
go.sum
Normal file
@@ -0,0 +1,6 @@
|
||||
github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3 h1:N8pCiqpJAHyOO8lx/F7HzyQ/qjwatmmW0zHTt0e/SeU=
|
||||
github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3/go.mod h1:4WK1jUpH8NFdDiv7IJcBfyCIOMqKjZ15kcw5eBKALvc=
|
||||
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0 h1:2pVgen9rh18IxSWxOa80bObcpyfrS6d5bJtZeCUN7rY=
|
||||
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0/go.mod h1:koVyx+gN3TfE70rpOidywETVODk87304YpwW69Y27J4=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
25
launcher_server.go
Normal file
25
launcher_server.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
func g6Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
http.ServeFile(w, r, "www/g6_launcher/index.html")
|
||||
|
||||
}
|
||||
|
||||
// serveLauncherHTML is responsible for serving the launcher HTML and (HACK) serverlist.xml.
|
||||
func serveLauncherHTML(listenAddr string) {
|
||||
// Manually route the folder root to index.html? Is there a better way to do this?
|
||||
router := httprouter.New()
|
||||
router.GET("/g6_launcher/", g6Index)
|
||||
|
||||
static := httprouter.New()
|
||||
static.ServeFiles("/*filepath", http.Dir("www"))
|
||||
router.NotFound = static
|
||||
|
||||
http.ListenAndServe(listenAddr, router)
|
||||
}
|
||||
20
main.go
Normal file
20
main.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
_ "time"
|
||||
|
||||
"github.com/Andoryuuta/Erupe/network"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Starting!")
|
||||
|
||||
go serveLauncherHTML(":80")
|
||||
go doSignServer(":53312")
|
||||
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
78
network/crypt_conn.go
Normal file
78
network/crypt_conn.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/Andoryuuta/Erupe/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
|
||||
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,
|
||||
sentPackets: 0,
|
||||
prevSendPacketCombinedCheck: 0,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
//fmt.Printf("Header: %s\n", hex.Dump(headerData))
|
||||
|
||||
// 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)
|
||||
return nil, errors.New("decrypted data checksum doesn't match header")
|
||||
}
|
||||
|
||||
_ = combinedCheck
|
||||
/*
|
||||
fmt.Printf("cc %X, c0 %X, c1 %X, c2 %X\n", combinedCheck, check0, check1, check2)
|
||||
fmt.Printf("cc %X, c0 %X, c1 %X, c2 %X\n", cph.PrevPacketCombinedCheck, cph.Check0, cph.Check1, cph.Check2)
|
||||
fmt.Printf("cph: %+v\n", cph)
|
||||
*/
|
||||
return out, nil
|
||||
}
|
||||
67
network/crypt_packet.go
Normal file
67
network/crypt_packet.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
// CryptPacketHeaderLength represents the byte-length of
|
||||
// an encrypted packet header.
|
||||
CryptPacketHeaderLength = 14
|
||||
)
|
||||
|
||||
// CryptPacketHeader represents the parsed information of an encrypted packet header.
|
||||
type CryptPacketHeader struct {
|
||||
Pf0 byte
|
||||
KeyRotDelta byte
|
||||
PacketNum uint16
|
||||
DataSize uint16
|
||||
PrevPacketCombinedCheck uint16
|
||||
Check0 uint16
|
||||
Check1 uint16
|
||||
Check2 uint16
|
||||
}
|
||||
|
||||
// NewCryptPacketHeader parses raw bytes into a CryptPacketHeader
|
||||
func NewCryptPacketHeader(data []byte) (*CryptPacketHeader, error) {
|
||||
var c = CryptPacketHeader{}
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
|
||||
var err error
|
||||
err = binary.Read(r, binary.BigEndian, &c.Pf0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(r, binary.BigEndian, &c.KeyRotDelta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(r, binary.BigEndian, &c.PacketNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(r, binary.BigEndian, &c.DataSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(r, binary.BigEndian, &c.PrevPacketCombinedCheck)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(r, binary.BigEndian, &c.Check0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(r, binary.BigEndian, &c.Check1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(r, binary.BigEndian, &c.Check2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
84
network/crypto/crypto.go
Normal file
84
network/crypto/crypto.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package crypto
|
||||
|
||||
var (
|
||||
_encryptKey = []byte{0x90, 0x51, 0x26, 0x25, 0x04, 0xBF, 0xCF, 0x4C, 0x92, 0x02, 0x52, 0x7A, 0x70, 0x1A, 0x41, 0x88, 0x8C, 0xC2, 0xCE, 0xB8, 0xF6, 0x57, 0x7E, 0xBA, 0x83, 0x63, 0x2C, 0x24, 0x9A, 0x67, 0x86, 0x0C, 0xBE, 0x72, 0xFD, 0xB6, 0x7B, 0x79, 0xB0, 0x22, 0x5A, 0x60, 0x5C, 0x4F, 0x49, 0xE2, 0x0E, 0xF5, 0x3A, 0x81, 0xAE, 0x11, 0x6B, 0xF0, 0xA1, 0x01, 0xE8, 0x65, 0x8D, 0x5B, 0xDC, 0xCC, 0x93, 0x18, 0xB3, 0xAB, 0x77, 0xF7, 0x8E, 0xEC, 0xEF, 0x05, 0x00, 0xCA, 0x4E, 0xA7, 0xBC, 0xB5, 0x10, 0xC6, 0x6C, 0xC0, 0xC4, 0xE5, 0x87, 0x3F, 0xC1, 0x82, 0x29, 0x96, 0x45, 0x73, 0x07, 0xCB, 0x43, 0xF9, 0xF3, 0x08, 0x89, 0xD0, 0x99, 0x6A, 0x3B, 0x37, 0x19, 0xD4, 0x40, 0xEA, 0xD7, 0x85, 0x16, 0x66, 0x1E, 0x9C, 0x39, 0xBB, 0xEE, 0x4A, 0x03, 0x8A, 0x36, 0x2D, 0x13, 0x1D, 0x56, 0x48, 0xC7, 0x0D, 0x59, 0xB2, 0x44, 0xA3, 0xFE, 0x8B, 0x32, 0x1B, 0x84, 0xA0, 0x2E, 0x62, 0x17, 0x42, 0xB9, 0x9B, 0x2B, 0x75, 0xD8, 0x1C, 0x3C, 0x4D, 0x76, 0x27, 0x6E, 0x28, 0xD3, 0x33, 0xC3, 0x21, 0xAF, 0x34, 0x23, 0xDD, 0x68, 0x9F, 0xF1, 0xAD, 0xE1, 0xB4, 0xE7, 0xA6, 0x74, 0x15, 0x4B, 0xFA, 0x3D, 0x5F, 0x7C, 0xDA, 0x2F, 0x0A, 0xE3, 0x7D, 0xC8, 0xB7, 0x12, 0x6F, 0x9E, 0xA9, 0x14, 0x53, 0x97, 0x8F, 0x64, 0xF4, 0xF8, 0xA2, 0xA4, 0x2A, 0xD2, 0x47, 0x9D, 0x71, 0xC5, 0xE9, 0x06, 0x98, 0x20, 0x54, 0x80, 0xAA, 0xF2, 0xAC, 0x50, 0xD6, 0x7F, 0xD9, 0xC9, 0xCD, 0x69, 0x46, 0x6D, 0x30, 0xB1, 0x58, 0x0B, 0x55, 0xD1, 0x5D, 0xD5, 0xBD, 0x31, 0xDE, 0xA5, 0xE4, 0x91, 0x0F, 0x61, 0x38, 0xDF, 0xA8, 0xE6, 0x3E, 0x1F, 0x35, 0xED, 0xDB, 0x94, 0xEB, 0x09, 0x5E, 0x95, 0xFB, 0xFC, 0xE0, 0x78, 0xFF}
|
||||
_decryptKey = []byte{0x48, 0x37, 0x09, 0x76, 0x04, 0x47, 0xCC, 0x5C, 0x61, 0xF8, 0xB3, 0xE0, 0x1F, 0x7F, 0x2E, 0xEB, 0x4E, 0x33, 0xB8, 0x7A, 0xBC, 0xAB, 0x6E, 0x8C, 0x3F, 0x68, 0x0D, 0x87, 0x93, 0x7B, 0x70, 0xF2, 0xCE, 0x9D, 0x27, 0xA0, 0x1B, 0x03, 0x02, 0x97, 0x99, 0x58, 0xC5, 0x90, 0x1A, 0x79, 0x8A, 0xB2, 0xDD, 0xE6, 0x86, 0x9B, 0x9F, 0xF3, 0x78, 0x67, 0xED, 0x72, 0x30, 0x66, 0x94, 0xAE, 0xF1, 0x55, 0x6A, 0x0E, 0x8D, 0x5E, 0x82, 0x5A, 0xDB, 0xC7, 0x7D, 0x2C, 0x75, 0xAC, 0x07, 0x95, 0x4A, 0x2B, 0xD4, 0x01, 0x0A, 0xBD, 0xCF, 0xE1, 0x7C, 0x15, 0xDF, 0x80, 0x28, 0x3B, 0x2A, 0xE3, 0xF9, 0xAF, 0x29, 0xEC, 0x8B, 0x19, 0xC0, 0x39, 0x6F, 0x1D, 0xA2, 0xDA, 0x65, 0x34, 0x50, 0xDC, 0x98, 0xB9, 0x0C, 0xC9, 0x21, 0x5B, 0xAA, 0x91, 0x96, 0x42, 0xFE, 0x25, 0x0B, 0x24, 0xB0, 0xB5, 0x16, 0xD6, 0xD0, 0x31, 0x57, 0x18, 0x88, 0x6D, 0x1E, 0x54, 0x0F, 0x62, 0x77, 0x85, 0x10, 0x3A, 0x44, 0xBF, 0x00, 0xEA, 0x08, 0x3E, 0xF6, 0xFA, 0x59, 0xBE, 0xCD, 0x64, 0x1C, 0x8F, 0x71, 0xC8, 0xBA, 0xA3, 0x89, 0x36, 0xC3, 0x83, 0xC4, 0xE8, 0xA9, 0x4B, 0xEF, 0xBB, 0xD1, 0x41, 0xD3, 0xA5, 0x32, 0x9E, 0x26, 0xDE, 0x81, 0x40, 0xA7, 0x4D, 0x23, 0xB7, 0x13, 0x8E, 0x17, 0x73, 0x4C, 0xE5, 0x20, 0x05, 0x51, 0x56, 0x11, 0x9C, 0x52, 0xCA, 0x4F, 0x7E, 0xB6, 0xD8, 0x49, 0x5D, 0x3D, 0xD9, 0x12, 0x06, 0x63, 0xE2, 0xC6, 0x9A, 0x69, 0xE4, 0xD5, 0x6C, 0x92, 0xD7, 0xB1, 0xF5, 0x3C, 0xA1, 0xE7, 0xEE, 0xFD, 0xA6, 0x2D, 0xB4, 0xE9, 0x53, 0xF0, 0xA8, 0x38, 0xCB, 0x6B, 0xF7, 0x45, 0xF4, 0x74, 0x46, 0x35, 0xA4, 0xD2, 0x60, 0xC1, 0x2F, 0x14, 0x43, 0xC2, 0x5F, 0xAD, 0xFB, 0xFC, 0x22, 0x84, 0xFF}
|
||||
_sharedCryptKey = []byte{0xDD, 0xA8, 0x5F, 0x1E, 0x57, 0xAF, 0xC0, 0xCC, 0x43, 0x35, 0x8F, 0xBB, 0x6F, 0xE6, 0xA1, 0xD6, 0x60, 0xB9, 0x1A, 0xAE, 0x20, 0x49, 0x24, 0x81, 0x21, 0xFE, 0x86, 0x2B, 0x98, 0xB7, 0xB3, 0xD2, 0x91, 0x01, 0x3A, 0x4C, 0x65, 0x92, 0x1C, 0xF4, 0xBE, 0xDD, 0xD9, 0x08, 0xE6, 0x81, 0x98, 0x1B, 0x8D, 0x60, 0xF3, 0x6F, 0xA1, 0x47, 0x24, 0xF1, 0x53, 0x45, 0xC8, 0x7B, 0x88, 0x80, 0x4E, 0x36, 0xC3, 0x0D, 0xC9, 0xD6, 0x8B, 0x08, 0x19, 0x0B, 0xA5, 0xC1, 0x11, 0x4C, 0x60, 0xF8, 0x5D, 0xFC, 0x15, 0x68, 0x7E, 0x32, 0xC0, 0x50, 0xAB, 0x64, 0x1F, 0x8A, 0xD4, 0x08, 0x39, 0x7F, 0xC2, 0xFB, 0xBA, 0x6C, 0xF0, 0xE6, 0xB0, 0x31, 0x10, 0xC1, 0xBF, 0x75, 0x43, 0xBB, 0x18, 0x04, 0x0D, 0xD1, 0x97, 0xF7, 0x23, 0x21, 0x83, 0x8B, 0xCA, 0x25, 0x2B, 0xA3, 0x03, 0x13, 0xEA, 0xAE, 0xFE, 0xF0, 0xEB, 0xFD, 0x85, 0x57, 0x53, 0x65, 0x41, 0x2A, 0x40, 0x99, 0xC0, 0x94, 0x65, 0x7E, 0x7C, 0x93, 0x82, 0xB0, 0xB3, 0xE5, 0xC0, 0x21, 0x09, 0x84, 0xD5, 0xEF, 0x9F, 0xD1, 0x7E, 0xDC, 0x4D, 0xF5, 0x7E, 0xCD, 0x45, 0x3C, 0x7F, 0xF5, 0x59, 0x98, 0xC6, 0x55, 0xFC, 0x9F, 0xA3, 0xB7, 0x74, 0xEE, 0x31, 0x98, 0xE6, 0xB7, 0xBE, 0x26, 0xF4, 0x3C, 0x76, 0xF1, 0x23, 0x7E, 0x02, 0x4E, 0x3C, 0xD1, 0xC7, 0x28, 0x23, 0x73, 0xC4, 0xD9, 0x5E, 0x0D, 0xA1, 0x80, 0xA5, 0xAA, 0x26, 0x0A, 0xA3, 0x44, 0x82, 0x74, 0xE6, 0x3C, 0x44, 0x27, 0x51, 0x0D, 0x5F, 0xC7, 0x9C, 0xD6, 0x63, 0x67, 0xA5, 0x27, 0x97, 0x38, 0xFB, 0x2D, 0xD3, 0xD6, 0x60, 0x25, 0x83, 0x4D, 0x37, 0x5B, 0x40, 0x59, 0x11, 0x77, 0x51, 0x11, 0x14, 0x18, 0x07, 0x63, 0xB1, 0x34, 0x3D, 0xB8, 0x60, 0x13, 0xC2, 0xE8, 0x13, 0x82}
|
||||
)
|
||||
|
||||
// Encrypt encrypts the given data using MHF's custom encryption+checksum method.
|
||||
// if a overrideByteKey value is supplied (!= nil), it will be used to override the derived/truncated key byte.
|
||||
func Encrypt(data []byte, key uint32, overrideByteKey *byte) (outputData []byte, combinedCheck uint16, check0 uint16, check1 uint16, check2 uint16) {
|
||||
return _generalCrypt(data, key, 0, overrideByteKey)
|
||||
}
|
||||
|
||||
// Decrypt decrypts the given data using MHF's custom decryption+checksum method.
|
||||
// if a overrideByteKey value is supplied (!= nil), it will be used to override the derived/truncated key byte.
|
||||
func Decrypt(data []byte, key uint32, overrideByteKey *byte) (outputData []byte, combinedCheck uint16, check0 uint16, check1 uint16, check2 uint16) {
|
||||
return _generalCrypt(data, key, 1, overrideByteKey)
|
||||
}
|
||||
|
||||
// _generalCrypt is a generalized MHF crypto function that can perform both encryption and decryption,
|
||||
// these two crypto operations are combined into a single function because they shared most of their logic.
|
||||
// encrypt: cryptType==0
|
||||
// decrypt: cryptType==1
|
||||
func _generalCrypt(data []byte, rotKey uint32, cryptType int, overrideByteKey *byte) ([]byte, uint16, uint16, uint16, uint16) {
|
||||
cryptKeyTruncByte := byte(((rotKey >> 1) % 999983) & 0xFF)
|
||||
if overrideByteKey != nil {
|
||||
cryptKeyTruncByte = *overrideByteKey
|
||||
}
|
||||
|
||||
derivedCryptKey := int32((uint32(len(data)) * uint32(cryptKeyTruncByte+1)) & 0xFFFFFFFF)
|
||||
sharedBufIdx := byte(1)
|
||||
accumulator0 := uint32(0)
|
||||
accumulator1 := uint32(0)
|
||||
accumulator2 := uint32(0)
|
||||
|
||||
var outputData []byte
|
||||
if cryptType == 0 {
|
||||
for i := 0; i < len(data); i++ {
|
||||
// Do the encryption for this iteration
|
||||
encKeyIdx := int32(((uint32(derivedCryptKey) >> 10) ^ uint32(data[i])) & 0xFF)
|
||||
derivedCryptKey = (0x4FD * (derivedCryptKey + 1))
|
||||
encKeyByte := _encryptKey[encKeyIdx]
|
||||
|
||||
// Update the checksum accumulators.
|
||||
accumulator2 = uint32((accumulator2 + (uint32(sharedBufIdx) * uint32(data[i]))) & 0xFFFFFFFF)
|
||||
accumulator1 = uint32((accumulator1 + uint32(encKeyIdx)) & 0xFFFFFFFF)
|
||||
accumulator0 = uint32((accumulator0 + (uint32(encKeyByte)<<(i&7))&0xFFFFFFFF) & 0xFFFFFFFF)
|
||||
|
||||
// Append the output.
|
||||
outputData = append(outputData, _sharedCryptKey[sharedBufIdx]^encKeyByte)
|
||||
|
||||
// Update the sharedBufIdx for the next iteration.
|
||||
sharedBufIdx = data[i]
|
||||
}
|
||||
|
||||
} else if cryptType == 1 {
|
||||
for i := 0; i < len(data); i++ {
|
||||
// Do the decryption for this iteration
|
||||
oldSharedBufIdx := sharedBufIdx
|
||||
tIdx := data[i] ^ _sharedCryptKey[sharedBufIdx]
|
||||
decKeyByte := _decryptKey[tIdx]
|
||||
sharedBufIdx = byte(((uint32(derivedCryptKey) >> 10) ^ uint32(decKeyByte)) & 0xFF)
|
||||
|
||||
// Update the checksum accumulators.
|
||||
accumulator0 = (accumulator0 + ((uint32(tIdx) << (i & 7)) & 0xFFFFFFFF))
|
||||
accumulator1 = (accumulator1 + uint32(decKeyByte)) & 0xFFFFFFFF
|
||||
accumulator2 = (accumulator2 + ((uint32(oldSharedBufIdx) * uint32(sharedBufIdx)) & 0xFFFFFFFF)) & 0xFFFFFFFF
|
||||
|
||||
// Append the output.
|
||||
outputData = append(outputData, sharedBufIdx)
|
||||
|
||||
// Update the key pos for next iteration.
|
||||
derivedCryptKey = (0x4FD * (derivedCryptKey + 1))
|
||||
}
|
||||
}
|
||||
|
||||
combinedCheck := uint16((accumulator1 + (accumulator0 >> 1) + (accumulator2 >> 2)) & 0xFFFF)
|
||||
check0 := uint16((accumulator0 ^ ((accumulator0 & 0xFFFF0000) >> 16)) & 0xFFFF)
|
||||
check1 := uint16((accumulator1 ^ ((accumulator1 & 0xFFFF0000) >> 16)) & 0xFFFF)
|
||||
check2 := uint16((accumulator2 ^ ((accumulator2 & 0xFFFF0000) >> 16)) & 0xFFFF)
|
||||
|
||||
return outputData, combinedCheck, check0, check1, check2
|
||||
}
|
||||
98
network/crypto/crypto_test.go
Normal file
98
network/crypto/crypto_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var commonTestData = []byte{0x74, 0x65, 0x73, 0x74}
|
||||
var tests = []struct {
|
||||
decryptedData []byte
|
||||
key uint32
|
||||
encryptedData []byte
|
||||
ecc, ec0, ec1, ec2 uint16
|
||||
}{
|
||||
{
|
||||
commonTestData,
|
||||
0,
|
||||
[]byte{0x46, 0x53, 0x28, 0x5E},
|
||||
0x2976, 0x06ea, 0x0215, 0x08FB3,
|
||||
},
|
||||
{
|
||||
commonTestData,
|
||||
3,
|
||||
[]byte{0x46, 0x95, 0x88, 0xEA},
|
||||
0x2AE4, 0x0A56, 0x01CD, 0x08FB3,
|
||||
},
|
||||
/*
|
||||
// TODO(Andoryuuta): This case fails. Debug the client and figure out if this is valid expected data.
|
||||
{
|
||||
commonTestData,
|
||||
995117,
|
||||
[]byte{0x46, 0x28, 0xFF, 0xAA},
|
||||
0x2A22, 0x09D4, 0x014C, 0x08FB3,
|
||||
},
|
||||
*/
|
||||
{
|
||||
commonTestData,
|
||||
0x7FFFFFFF,
|
||||
[]byte{0x46, 0x53, 0x28, 0x5E},
|
||||
0x2976, 0x06ea, 0x0215, 0x08FB3,
|
||||
},
|
||||
{
|
||||
commonTestData,
|
||||
0x80000000,
|
||||
[]byte{0x46, 0x95, 0x88, 0xEA},
|
||||
0x2AE4, 0x0A56, 0x01CD, 0x08FB3,
|
||||
},
|
||||
{
|
||||
commonTestData,
|
||||
0xFFFFFFFF,
|
||||
[]byte{0x46, 0xB5, 0xDC, 0xB2},
|
||||
0x2ADD, 0x09A6, 0x021E, 0x08FB3,
|
||||
},
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
for k, tt := range tests {
|
||||
testname := fmt.Sprintf("encrypt_test_%d", k)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
out, cc, c0, c1, c2 := Encrypt(tt.decryptedData, tt.key, nil)
|
||||
if cc != tt.ecc {
|
||||
t.Errorf("got cc 0x%X, want 0x%X", cc, tt.ecc)
|
||||
} else if c0 != tt.ec0 {
|
||||
t.Errorf("got c0 0x%X, want 0x%X", c0, tt.ec0)
|
||||
} else if c1 != tt.ec1 {
|
||||
t.Errorf("got c1 0x%X, want 0x%X", c1, tt.ec1)
|
||||
} else if c2 != tt.ec2 {
|
||||
t.Errorf("got c2 0x%X, want 0x%X", c2, tt.ec2)
|
||||
} else if !bytes.Equal(out, tt.encryptedData) {
|
||||
t.Errorf("got out\n\t%s\nwant\n\t%s", hex.Dump(out), hex.Dump(tt.encryptedData))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
for k, tt := range tests {
|
||||
testname := fmt.Sprintf("decrypt_test_%d", k)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
out, cc, c0, c1, c2 := Decrypt(tt.encryptedData, tt.key, nil)
|
||||
if cc != tt.ecc {
|
||||
t.Errorf("got cc 0x%X, want 0x%X", cc, tt.ecc)
|
||||
} else if c0 != tt.ec0 {
|
||||
t.Errorf("got c0 0x%X, want 0x%X", c0, tt.ec0)
|
||||
} else if c1 != tt.ec1 {
|
||||
t.Errorf("got c1 0x%X, want 0x%X", c1, tt.ec1)
|
||||
} else if c2 != tt.ec2 {
|
||||
t.Errorf("got c2 0x%X, want 0x%X", c2, tt.ec2)
|
||||
} else if !bytes.Equal(out, tt.decryptedData) {
|
||||
t.Errorf("got out\n\t%s\nwant\n\t%s", hex.Dump(out), hex.Dump(tt.decryptedData))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
56
sign_server.go
Normal file
56
sign_server.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/Andoryuuta/Erupe/network"
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
)
|
||||
|
||||
func handleSignServerConnection(conn net.Conn) {
|
||||
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
||||
nullInit := make([]byte, 8)
|
||||
n, err := io.ReadFull(conn, nullInit)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
} else if n != len(nullInit) {
|
||||
fmt.Println("io.ReadFull couldn't read the full 8 byte init.")
|
||||
return
|
||||
}
|
||||
|
||||
cc := network.NewCryptConn(conn)
|
||||
for {
|
||||
pkt, err := cc.ReadPacket()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrameFromBytes(pkt)
|
||||
loginType := string(bf.ReadNullTerminatedBytes())
|
||||
username := string(bf.ReadNullTerminatedBytes())
|
||||
password := string(bf.ReadNullTerminatedBytes())
|
||||
unk := string(bf.ReadNullTerminatedBytes())
|
||||
fmt.Println("Got signin, type: %s, username: %s, password %s, unk: %s", loginType, username, password, unk)
|
||||
|
||||
//fmt.Printf("Got:\n%s", hex.Dump(pkt))
|
||||
}
|
||||
}
|
||||
|
||||
func doSignServer(listenAddr string) {
|
||||
l, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go handleSignServerConnection(conn)
|
||||
}
|
||||
}
|
||||
97
test.py
Normal file
97
test.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from hexdump import hexdump
|
||||
import sys
|
||||
|
||||
ENCRYPT_KEY = b'\x90\x51\x26\x25\x04\xBF\xCF\x4C\x92\x02\x52\x7A\x70\x1A\x41\x88\x8C\xC2\xCE\xB8\xF6\x57\x7E\xBA\x83\x63\x2C\x24\x9A\x67\x86\x0C\xBE\x72\xFD\xB6\x7B\x79\xB0\x22\x5A\x60\x5C\x4F\x49\xE2\x0E\xF5\x3A\x81\xAE\x11\x6B\xF0\xA1\x01\xE8\x65\x8D\x5B\xDC\xCC\x93\x18\xB3\xAB\x77\xF7\x8E\xEC\xEF\x05\x00\xCA\x4E\xA7\xBC\xB5\x10\xC6\x6C\xC0\xC4\xE5\x87\x3F\xC1\x82\x29\x96\x45\x73\x07\xCB\x43\xF9\xF3\x08\x89\xD0\x99\x6A\x3B\x37\x19\xD4\x40\xEA\xD7\x85\x16\x66\x1E\x9C\x39\xBB\xEE\x4A\x03\x8A\x36\x2D\x13\x1D\x56\x48\xC7\x0D\x59\xB2\x44\xA3\xFE\x8B\x32\x1B\x84\xA0\x2E\x62\x17\x42\xB9\x9B\x2B\x75\xD8\x1C\x3C\x4D\x76\x27\x6E\x28\xD3\x33\xC3\x21\xAF\x34\x23\xDD\x68\x9F\xF1\xAD\xE1\xB4\xE7\xA6\x74\x15\x4B\xFA\x3D\x5F\x7C\xDA\x2F\x0A\xE3\x7D\xC8\xB7\x12\x6F\x9E\xA9\x14\x53\x97\x8F\x64\xF4\xF8\xA2\xA4\x2A\xD2\x47\x9D\x71\xC5\xE9\x06\x98\x20\x54\x80\xAA\xF2\xAC\x50\xD6\x7F\xD9\xC9\xCD\x69\x46\x6D\x30\xB1\x58\x0B\x55\xD1\x5D\xD5\xBD\x31\xDE\xA5\xE4\x91\x0F\x61\x38\xDF\xA8\xE6\x3E\x1F\x35\xED\xDB\x94\xEB\x09\x5E\x95\xFB\xFC\xE0\x78\xFF'
|
||||
DECRYPT_KEY = b'\x48\x37\x09\x76\x04\x47\xCC\x5C\x61\xF8\xB3\xE0\x1F\x7F\x2E\xEB\x4E\x33\xB8\x7A\xBC\xAB\x6E\x8C\x3F\x68\x0D\x87\x93\x7B\x70\xF2\xCE\x9D\x27\xA0\x1B\x03\x02\x97\x99\x58\xC5\x90\x1A\x79\x8A\xB2\xDD\xE6\x86\x9B\x9F\xF3\x78\x67\xED\x72\x30\x66\x94\xAE\xF1\x55\x6A\x0E\x8D\x5E\x82\x5A\xDB\xC7\x7D\x2C\x75\xAC\x07\x95\x4A\x2B\xD4\x01\x0A\xBD\xCF\xE1\x7C\x15\xDF\x80\x28\x3B\x2A\xE3\xF9\xAF\x29\xEC\x8B\x19\xC0\x39\x6F\x1D\xA2\xDA\x65\x34\x50\xDC\x98\xB9\x0C\xC9\x21\x5B\xAA\x91\x96\x42\xFE\x25\x0B\x24\xB0\xB5\x16\xD6\xD0\x31\x57\x18\x88\x6D\x1E\x54\x0F\x62\x77\x85\x10\x3A\x44\xBF\x00\xEA\x08\x3E\xF6\xFA\x59\xBE\xCD\x64\x1C\x8F\x71\xC8\xBA\xA3\x89\x36\xC3\x83\xC4\xE8\xA9\x4B\xEF\xBB\xD1\x41\xD3\xA5\x32\x9E\x26\xDE\x81\x40\xA7\x4D\x23\xB7\x13\x8E\x17\x73\x4C\xE5\x20\x05\x51\x56\x11\x9C\x52\xCA\x4F\x7E\xB6\xD8\x49\x5D\x3D\xD9\x12\x06\x63\xE2\xC6\x9A\x69\xE4\xD5\x6C\x92\xD7\xB1\xF5\x3C\xA1\xE7\xEE\xFD\xA6\x2D\xB4\xE9\x53\xF0\xA8\x38\xCB\x6B\xF7\x45\xF4\x74\x46\x35\xA4\xD2\x60\xC1\x2F\x14\x43\xC2\x5F\xAD\xFB\xFC\x22\x84\xFF'
|
||||
SHARED_CRYPT_KEY = b'\xDD\xA8\x5F\x1E\x57\xAF\xC0\xCC\x43\x35\x8F\xBB\x6F\xE6\xA1\xD6\x60\xB9\x1A\xAE\x20\x49\x24\x81\x21\xFE\x86\x2B\x98\xB7\xB3\xD2\x91\x01\x3A\x4C\x65\x92\x1C\xF4\xBE\xDD\xD9\x08\xE6\x81\x98\x1B\x8D\x60\xF3\x6F\xA1\x47\x24\xF1\x53\x45\xC8\x7B\x88\x80\x4E\x36\xC3\x0D\xC9\xD6\x8B\x08\x19\x0B\xA5\xC1\x11\x4C\x60\xF8\x5D\xFC\x15\x68\x7E\x32\xC0\x50\xAB\x64\x1F\x8A\xD4\x08\x39\x7F\xC2\xFB\xBA\x6C\xF0\xE6\xB0\x31\x10\xC1\xBF\x75\x43\xBB\x18\x04\x0D\xD1\x97\xF7\x23\x21\x83\x8B\xCA\x25\x2B\xA3\x03\x13\xEA\xAE\xFE\xF0\xEB\xFD\x85\x57\x53\x65\x41\x2A\x40\x99\xC0\x94\x65\x7E\x7C\x93\x82\xB0\xB3\xE5\xC0\x21\x09\x84\xD5\xEF\x9F\xD1\x7E\xDC\x4D\xF5\x7E\xCD\x45\x3C\x7F\xF5\x59\x98\xC6\x55\xFC\x9F\xA3\xB7\x74\xEE\x31\x98\xE6\xB7\xBE\x26\xF4\x3C\x76\xF1\x23\x7E\x02\x4E\x3C\xD1\xC7\x28\x23\x73\xC4\xD9\x5E\x0D\xA1\x80\xA5\xAA\x26\x0A\xA3\x44\x82\x74\xE6\x3C\x44\x27\x51\x0D\x5F\xC7\x9C\xD6\x63\x67\xA5\x27\x97\x38\xFB\x2D\xD3\xD6\x60\x25\x83\x4D\x37\x5B\x40\x59\x11\x77\x51\x11\x14\x18\x07\x63\xB1\x34\x3D\xB8\x60\x13\xC2\xE8\x13\x82'
|
||||
|
||||
class PacketCrypto(object):
|
||||
def encrypt(data, rot_key, override_byte_key=None):
|
||||
return PacketCrypto._general_crypt(data, rot_key, 0, override_byte_key)
|
||||
|
||||
def decrypt(data, rot_key, override_byte_key=None):
|
||||
return PacketCrypto._general_crypt(data, rot_key, 1, override_byte_key)
|
||||
|
||||
def _general_crypt(data, rot_key, crypt_type, override_byte_key=None):
|
||||
"""A generic crypto function for both encryption and decryption
|
||||
|
||||
:param data: input data
|
||||
:param rot_key: crypto key index rotation
|
||||
:param crypt_type: determines whether to encrypt(0) or decrypt(1)
|
||||
:param override_byte_key: override value for the truncated rotation index byte
|
||||
:type data: bytes
|
||||
:type rot_key: int
|
||||
:type crypt_type: int
|
||||
:type override_byte_key: bool
|
||||
"""
|
||||
unk_cryptkey_rot_arg = ((rot_key >> 1) % 999983) & 0xFF
|
||||
print("unk_cryptkey_rot_arg: {:X}".format(unk_cryptkey_rot_arg))
|
||||
|
||||
|
||||
if override_byte_key is not None:
|
||||
unk_cryptkey_rot_arg = override_byte_key
|
||||
|
||||
unk_derived_cryptkey_rot = (len(data) * (unk_cryptkey_rot_arg+1)) & 0xFFFFFFFF
|
||||
shared_buf_idx = 1
|
||||
accumulator_0 = 0
|
||||
accumulator_1 = 0
|
||||
accumulator_2 = 0
|
||||
|
||||
print('unk_derived_cryptkey_rot: {:X}'.format(unk_derived_cryptkey_rot))
|
||||
|
||||
output_data = bytearray()
|
||||
if crypt_type == 0: # Encrypt
|
||||
for i in range(len(data)):
|
||||
print("\n")
|
||||
# Do the encryption for this iteration
|
||||
enc_key_idx = ((unk_derived_cryptkey_rot >> 10) ^ data[i]) & 0xFF
|
||||
print('enc_key_idx: {:X}'.format(enc_key_idx))
|
||||
unk_derived_cryptkey_rot = (0x4FD * (unk_derived_cryptkey_rot + 1)) & 0xFFFFFFFF
|
||||
print('unk_derived_cryptkey_rot: {:X}'.format(unk_derived_cryptkey_rot))
|
||||
enc_key_byte = ENCRYPT_KEY[enc_key_idx]
|
||||
print('enc_key_byte: {:X}'.format(enc_key_byte))
|
||||
|
||||
# Update the checksum accumulators.
|
||||
accumulator_2 = (accumulator_2 + (shared_buf_idx * data[i])) & 0xFFFFFFFF
|
||||
accumulator_1 = (accumulator_1 + enc_key_idx) & 0xFFFFFFFF
|
||||
accumulator_0 = (accumulator_0 + (enc_key_byte << (i & 7)) & 0xFFFFFFFF) & 0xFFFFFFFF
|
||||
|
||||
# Append the output.
|
||||
output_data.append(SHARED_CRYPT_KEY[shared_buf_idx] ^ enc_key_byte)
|
||||
|
||||
# Update the shared_buf_idx for the next iteration.
|
||||
shared_buf_idx = data[i]
|
||||
elif crypt_type == 1: # Decrypt
|
||||
for i in range(len(data)):
|
||||
# Do the decryption for this iteration
|
||||
old_shared_buf_idx = shared_buf_idx
|
||||
t_idx = data[i] ^ SHARED_CRYPT_KEY[shared_buf_idx]
|
||||
dec_key_byte = DECRYPT_KEY[t_idx]
|
||||
shared_buf_idx = ((unk_derived_cryptkey_rot >> 10) ^ dec_key_byte) & 0xFF
|
||||
|
||||
# Update the checksum accumulators.
|
||||
accumulator_0 = (accumulator_0 + ((t_idx << (i & 7)) & 0xFFFFFFFF))
|
||||
accumulator_1 = (accumulator_1 + dec_key_byte) & 0xFFFFFFFF
|
||||
accumulator_2 = (accumulator_2 + ((old_shared_buf_idx * shared_buf_idx)&0xFFFFFFFF)) & 0xFFFFFFFF
|
||||
|
||||
# Append the output.
|
||||
output_data.append(shared_buf_idx)
|
||||
|
||||
# Update the key pos for next iteration.
|
||||
unk_derived_cryptkey_rot = (0x4FD * (unk_derived_cryptkey_rot + 1)) & 0xFFFFFFFF
|
||||
else:
|
||||
raise Exception("Unknown crypt_type value.")
|
||||
|
||||
combined_check = (accumulator_1 + (accumulator_0 >> 1) + (accumulator_2 >> 2)) & 0xFFFF
|
||||
check_0 = (accumulator_0 ^ ((accumulator_0&0xFFFF0000)>>16)) & 0xFFFF
|
||||
check_1 = (accumulator_1 ^ ((accumulator_1&0xFFFF0000)>>16)) & 0xFFFF
|
||||
check_2 = (accumulator_2 ^ ((accumulator_2&0xFFFF0000)>>16)) & 0xFFFF
|
||||
|
||||
return (output_data, combined_check, check_0, check_1, check_2)
|
||||
|
||||
|
||||
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
(output_data, combined_check, check_0, check_1, check_2) = PacketCrypto._general_crypt(f.read(), int(sys.argv[2], 16), 1)
|
||||
hexdump(output_data)
|
||||
print("cc {:x}, c0 {:x}, c1 {:x}, c2 {:x}".format(combined_check, check_0, check_1, check_2))
|
||||
Reference in New Issue
Block a user