initial commit

This commit is contained in:
Jonathan Barrow 2021-10-24 12:38:23 -04:00
commit 07f4e2473d
7 changed files with 313 additions and 0 deletions

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# custom
build
.env

41
database.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"context"
"os"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mongoClient *mongo.Client
var mongoContext context.Context
var mongoDatabase *mongo.Database
var mongoCollection *mongo.Collection
func connectMongo() {
mongoClient, _ = mongo.NewClient(options.Client().ApplyURI(os.Getenv("MONGO_URI")))
mongoContext, _ = context.WithTimeout(context.Background(), 10*time.Second)
_ = mongoClient.Connect(mongoContext)
mongoDatabase = mongoClient.Database("pretendo")
mongoCollection = mongoDatabase.Collection("nexaccounts")
}
func getNEXAccountByPID(pid uint32) bson.M {
var result bson.M
err := mongoCollection.FindOne(context.TODO(), bson.D{{Key: "pid", Value: pid}}, options.FindOne()).Decode(&result)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil
}
panic(err)
}
return result
}

22
init.go Normal file
View File

@ -0,0 +1,22 @@
package main
import (
"log"
"github.com/joho/godotenv"
)
type Config struct {
Mongo struct {
}
Cassandra struct{}
}
func init() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
connectMongo()
}

79
kerberos.go Normal file
View File

@ -0,0 +1,79 @@
package main
import (
nex "github.com/PretendoNetwork/nex-go"
)
func generateKerberosTicket(userPID uint32, serverPID uint32, keySize int) ([]byte, int) {
user := getNEXAccountByPID(userPID)
if user == nil {
return []byte{}, 0x80030064 // RendezVous::InvalidUsername
}
userPassword := user["password"].(string)
serverPassword := "password"
// Create session key and ticket keys
sessionKey := make([]byte, keySize)
ticketInfoKey := make([]byte, 16) // key for encrypting the internal ticket info. Only used by server. TODO: Make this random!
userKey := deriveKey(userPID, []byte(userPassword)) // Key for encrypting entire ticket. Used by client and server
serverKey := deriveKey(serverPID, []byte(serverPassword))
finalKey := nex.MD5Hash(append(serverKey, ticketInfoKey...))
//rand.Read(sessionKey) // Create a random session key
//fmt.Println("Using Session Key: " + hex.EncodeToString(sessionKey))
////////////////////////////////
// Build internal ticket info //
////////////////////////////////
expiration := nex.NewDateTime(0)
ticketInfoStream := nex.NewStreamOut(nexServer)
ticketInfoStream.WriteUInt64LE(expiration.Now())
ticketInfoStream.WriteUInt32LE(userPID)
ticketInfoStream.Grow(int64(keySize))
ticketInfoStream.WriteBytesNext(sessionKey)
// Encrypt internal ticket info
ticketInfoEncryption := nex.NewKerberosEncryption(nex.MD5Hash(finalKey))
encryptedTicketInfo := ticketInfoEncryption.Encrypt(ticketInfoStream.Bytes())
///////////////////////////////////
// Build ticket data New Version //
///////////////////////////////////
ticketDataStream := nex.NewStreamOut(nexServer)
ticketDataStream.WriteBuffer(ticketInfoKey)
ticketDataStream.WriteBuffer(encryptedTicketInfo)
///////////////////////////
// Build Kerberos Ticket //
///////////////////////////
ticketStream := nex.NewStreamOut(nexServer)
// Write session key
ticketStream.Grow(int64(keySize))
ticketStream.WriteBytesNext(sessionKey)
ticketStream.WriteUInt32LE(serverPID)
ticketStream.WriteBuffer(ticketDataStream.Bytes())
// Encrypt the ticket
ticketEncryption := nex.NewKerberosEncryption(userKey)
encryptedTicket := ticketEncryption.Encrypt(ticketStream.Bytes())
return encryptedTicket, 0
}
func deriveKey(pid uint32, password []byte) []byte {
for i := 0; i < 65000+int(pid)%1024; i++ {
password = nex.MD5Hash(password)
}
return password
}

69
login_ex.go Normal file
View File

@ -0,0 +1,69 @@
package main
import (
"fmt"
"strconv"
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func loginEx(err error, client *nex.Client, callID uint32, username string, authenticationInfo *nexproto.AuthenticationInfo) {
// TODO: Verify auth info
if err != nil {
fmt.Println(err)
return
}
userPID, _ := strconv.Atoi(username)
serverPID := 1 // Quazal Rendez-Vous
encryptedTicket, errorCode := generateKerberosTicket(uint32(userPID), uint32(serverPID), nexServer.KerberosKeySize())
if errorCode != 0 {
fmt.Println(errorCode)
return
}
// Build the response body
stationURL := "prudps:/address=66.177.0.8;port=60005;CID=1;PID=2;sid=1;stream=10;type=2"
serverName := "Pretendo WiiU Chat"
rvConnectionData := nex.NewRVConnectionData()
rvConnectionData.SetStationURL(stationURL)
rvConnectionData.SetSpecialProtocols([]byte{})
rvConnectionData.SetStationURLSpecialProtocols("")
serverTime := nex.NewDateTime(0)
rvConnectionData.SetTime(serverTime.Now())
rmcResponseStream := nex.NewStreamOut(nexServer)
rmcResponseStream.WriteUInt32LE(0x10001) // success
rmcResponseStream.WriteUInt32LE(uint32(userPID))
rmcResponseStream.WriteBuffer(encryptedTicket)
rmcResponseStream.WriteStructure(rvConnectionData)
rmcResponseStream.WriteString(serverName)
rmcResponseBody := rmcResponseStream.Bytes()
// Build response packet
rmcResponse := nex.NewRMCResponse(nexproto.AuthenticationProtocolID, callID)
rmcResponse.SetSuccess(nexproto.AuthenticationMethodLoginEx, rmcResponseBody)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV1(client, nil)
responsePacket.SetVersion(1)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}

34
main.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"fmt"
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
var nexServer *nex.Server
func main() {
nexServer = nex.NewServer()
nexServer.SetPrudpVersion(1)
nexServer.SetNexVersion(2)
nexServer.SetKerberosKeySize(32)
nexServer.SetAccessKey("e7a47214")
nexServer.On("Data", func(packet *nex.PacketV1) {
request := packet.RMCRequest()
fmt.Println("==WiiU Chat - Auth==")
fmt.Printf("Protocol ID: %#v\n", request.ProtocolID())
fmt.Printf("Method ID: %#v\n", request.MethodID())
fmt.Println("====================")
})
authenticationServer := nexproto.NewAuthenticationProtocol(nexServer)
authenticationServer.LoginEx(loginEx)
authenticationServer.RequestTicket(requestTicket)
nexServer.Listen(":60004")
}

49
request_ticket.go Normal file
View File

@ -0,0 +1,49 @@
package main
import (
"fmt"
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func requestTicket(err error, client *nex.Client, callID uint32, userPID uint32, serverPID uint32) {
if err != nil {
fmt.Println(err)
return
}
encryptedTicket, errorCode := generateKerberosTicket(userPID, serverPID, nexServer.KerberosKeySize())
if errorCode != 0 {
fmt.Println(errorCode)
return
}
// Build the response body
rmcResponseStream := nex.NewStreamOut(nexServer)
rmcResponseStream.WriteUInt32LE(0x10001) // success
rmcResponseStream.WriteBuffer(encryptedTicket)
rmcResponseBody := rmcResponseStream.Bytes()
// Build response packet
rmcResponse := nex.NewRMCResponse(nexproto.AuthenticationProtocolID, callID)
rmcResponse.SetSuccess(nexproto.AuthenticationMethodRequestTicket, rmcResponseBody)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV1(client, nil)
responsePacket.SetVersion(1)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}