3DS friends goes online

This commit is contained in:
Jonathan Barrow
2021-09-11 23:59:07 -04:00
parent b35f144c57
commit d09fac22b2
12 changed files with 441 additions and 13 deletions

View File

@@ -17,23 +17,30 @@ import (
func decryptToken(encryptedToken []byte) (*nexToken, error) {
// Split the encoded token into it's parts
cryptoConfig := encryptedToken[:0x90]
signature := encryptedToken[0x90:0xA4]
encryptedBody := encryptedToken[0xA4:]
cryptoConfig := encryptedToken[:0x82]
signature := encryptedToken[0x82:0x96]
encryptedBody := encryptedToken[0x96:]
// Parse crypto config into parts
encryptedAESKey := cryptoConfig[:128]
iv := cryptoConfig[128:]
point1 := cryptoConfig[128]
point2 := cryptoConfig[129]
// Rebuild the IV
iv := make([]byte, 0)
iv = append(iv, encryptedAESKey[point1:point1+8]...)
iv = append(iv, encryptedAESKey[point2:point2+8]...)
// Decrypt the AES key
decryptedAESKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, rsaPrivateKey, encryptedAESKey, nil)
if err != nil {
return &nexToken{}, err
return nil, err
}
// Decrypt the token body
block, err := aes.NewCipher(decryptedAESKey)
if err != nil {
return &nexToken{}, err
return nil, err
}
decryptedBody := make([]byte, len(encryptedBody))
@@ -45,7 +52,7 @@ func decryptToken(encryptedToken []byte) (*nexToken, error) {
// Verify the token body
err = verifySignature(decryptedBody, signature, hmacSecret)
if err != nil {
return &nexToken{}, err
return nil, err
}
// Unpack the token body to struct
@@ -54,7 +61,7 @@ func decryptToken(encryptedToken []byte) (*nexToken, error) {
err = binary.Read(tokenReader, binary.LittleEndian, token)
if err != nil {
return &nexToken{}, err
return nil, err
}
return token, nil

16
main.go
View File

@@ -29,25 +29,31 @@ func main() {
secureServer = nexproto.NewSecureProtocol(nexServer)
accountManagementServer := nexproto.NewAccountManagementProtocol(nexServer)
friendsServer := nexproto.NewFriendsProtocol(nexServer)
friends3DSServer := nexproto.NewFriends3DSProtocol(nexServer)
// Handle PRUDP CONNECT packet (not an RMC method)
nexServer.On("Connect", connect)
// Account Management protocol handles
accountManagementServer.NintendoCreateAccount(nintendoCreateAccount)
accountManagementServer.NintendoCreateAccount3DS(nintendoCreateAccount3DS)
// Secure protocol handles
// Handle RegisterEx RMC method
secureServer.Register(register)
secureServer.RegisterEx(registerEx)
// Friends (WiiU) protocol handles
friendsServer.UpdateAndGetAllInformation(updateAndGetAllInformation)
friendsServer.CheckSettingStatus(checkSettingStatus)
// Friends (3DS) protocol handles
friends3DSServer.UpdateProfile(updateProfile)
friends3DSServer.UpdateMii(updateMii)
friends3DSServer.UpdatePreference(updatePreference)
friends3DSServer.SyncFriend(syncFriend)
friends3DSServer.UpdatePresence(updatePresence)
friends3DSServer.UpdateFavoriteGameKey(updateFavoriteGameKey)
friends3DSServer.UpdateComment(updateComment)
nexServer.Listen(":60001")
}

View File

@@ -12,6 +12,10 @@ import (
)
func nintendoCreateAccount(err error, client *nex.Client, callID uint32, username string, key string, groups uint32, email string, nintendoCreateAccountData *nexproto.NintendoCreateAccountData) {
if err != nil {
panic(err)
}
tokenBase64 := nintendoCreateAccountData.Token
encryptedToken, _ := base64.StdEncoding.DecodeString(tokenBase64)

View File

@@ -0,0 +1,69 @@
package main
import (
"crypto/hmac"
"crypto/md5"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"fmt"
"strings"
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func nintendoCreateAccount3DS(err error, client *nex.Client, callID uint32, strPrincipalName string, strKey string, uiGroups uint32, strEmail string, oAuthData *nexproto.AccountExtraInfo) {
if err != nil {
panic(err)
}
fmt.Println(oAuthData.NEXToken)
tokenBase64 := oAuthData.NEXToken
tokenBase64 = strings.Replace(tokenBase64, ".", "+", -1)
tokenBase64 = strings.Replace(tokenBase64, "-", "/", -1)
tokenBase64 = strings.Replace(tokenBase64, "*", "=", -1)
encryptedToken, _ := base64.StdEncoding.DecodeString(tokenBase64)
decryptedToken, err := decryptToken(encryptedToken)
if err != nil {
panic(err)
}
pid := decryptedToken.UserPID
pidByteArray := make([]byte, 4)
binary.LittleEndian.PutUint32(pidByteArray, pid)
mac := hmac.New(md5.New, []byte(strKey))
mac.Write(pidByteArray)
pidHmac := hex.EncodeToString(mac.Sum(nil))
rmcResponseStream := nex.NewStreamOut(nexServer)
rmcResponseStream.WriteUInt32LE(pid)
rmcResponseStream.WriteString(pidHmac)
rmcResponseBody := rmcResponseStream.Bytes()
rmcResponse := nex.NewRMCResponse(nexproto.AccountManagementProtocolID, callID)
rmcResponse.SetSuccess(nexproto.AccountManagementMethodNintendoCreateAccount, rmcResponseBody)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}

140
sig.go Normal file
View File

@@ -0,0 +1,140 @@
package main
/*
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"strings"
)
type nexToken struct {
SystemType uint8
TokenType uint8
UserPID uint32
TitleID uint64
CreatTime uint64
}
var rsaPrivateKeyBytes []byte
var rsaPrivateKey *rsa.PrivateKey
var hmacSecret []byte
func parseRsaPrivateKey(keyBytes []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(keyBytes)
if block == nil {
return nil, errors.New("Failed to parse RSA key")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return privateKey, nil
}
func verifySignature(body []byte, expectedSignature []byte, secret []byte) error {
mac := hmac.New(sha1.New, secret)
mac.Write(body)
calculatedSignature := mac.Sum(nil)
if !bytes.Equal(expectedSignature, calculatedSignature) {
return errors.New("[ERROR] Calculated signature did not match")
}
return nil
}
func init() {
var err error
rsaPrivateKeyBytes, err = ioutil.ReadFile("private.pem")
if err != nil {
panic(err)
}
rsaPrivateKey, err = parseRsaPrivateKey(rsaPrivateKeyBytes)
if err != nil {
panic(err)
}
hmacSecret, err = ioutil.ReadFile("secret.key")
if err != nil {
panic(err)
}
}
func main() {
tokenBase64 := "Smbh6g2tL93Ro6RAvCBCGtLzGw.40vhPIjGNyZh9LkineqBFQ-PJGVXZ8Xg.hUZk92KTtrisA0.OnzGi92K-tMud5piu08XWa7hwSaxXNGjRJ.o6HWwi1xapFndyhWOHs5kXtmUiFmyc1AOlcfo8nvSHsPTsEDX2Qc9BeooBJrUeMxDc2LDch0HyiMKGdPnpBg2-Lpekma6fNvCg0ylTqUMqonI.smHCXikr4IhWvvwy9QZDJLM*"
tokenBase64 = strings.Replace(tokenBase64, ".", "+", -1)
tokenBase64 = strings.Replace(tokenBase64, "-", "/", -1)
tokenBase64 = strings.Replace(tokenBase64, "*", "=", -1)
encryptedToken, _ := base64.StdEncoding.DecodeString(tokenBase64)
// Split the encoded token into it's parts
cryptoConfig := encryptedToken[:0x82]
signature := encryptedToken[0x82:0x96]
encryptedBody := encryptedToken[0x96:]
// Parse crypto config into parts
encryptedAESKey := cryptoConfig[:128]
point1 := cryptoConfig[128]
point2 := cryptoConfig[129]
// Rebuild the IV
iv := make([]byte, 0)
iv = append(iv, encryptedAESKey[point1:point1+8]...)
iv = append(iv, encryptedAESKey[point2:point2+8]...)
// Decrypt the AES key
decryptedAESKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, rsaPrivateKey, encryptedAESKey, nil)
if err != nil {
panic(err)
}
// Decrypt the token body
block, err := aes.NewCipher(decryptedAESKey)
if err != nil {
panic(err)
}
decryptedBody := make([]byte, len(encryptedBody))
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(decryptedBody, encryptedBody)
decryptedBody = decryptedBody[:0x16] // Remove AES padding
// Verify the token body
err = verifySignature(decryptedBody, signature, hmacSecret)
if err != nil {
panic(err)
}
// Unpack the token body to struct
token := &nexToken{}
tokenReader := bytes.NewBuffer(decryptedBody)
err = binary.Read(tokenReader, binary.LittleEndian, token)
if err != nil {
panic(err)
}
fmt.Println(token)
}
*/

34
sync_friend.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import (
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func syncFriend(err error, client *nex.Client, callID uint32, unknown1 uint64, pids []uint32, unknown3 []uint64) {
// TODO: Do something with this
rmcResponseStream := nex.NewStreamOut(nexServer)
rmcResponseStream.WriteUInt32LE(0) // List<FriendRelationship> length 0
rmcResponseBody := rmcResponseStream.Bytes()
rmcResponse := nex.NewRMCResponse(nexproto.Friends3DSProtocolID, callID)
rmcResponse.SetSuccess(nexproto.Friends3DSMethodSyncFriend, rmcResponseBody)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}

28
update_comment.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func updateComment(err error, client *nex.Client, callID uint32, comment string) {
// TODO: Do something with this
rmcResponse := nex.NewRMCResponse(nexproto.Friends3DSProtocolID, callID)
rmcResponse.SetSuccess(nexproto.Friends3DSMethodUpdateComment, nil)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}

View File

@@ -0,0 +1,28 @@
package main
import (
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func updateFavoriteGameKey(err error, client *nex.Client, callID uint32, gameKey *nexproto.GameKey) {
// TODO: Do something with this
rmcResponse := nex.NewRMCResponse(nexproto.Friends3DSProtocolID, callID)
rmcResponse.SetSuccess(nexproto.Friends3DSMethodUpdateFavoriteGameKey, nil)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}

28
update_mii.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func updateMii(err error, client *nex.Client, callID uint32, mii *nexproto.Mii) {
// TODO: Do something with this
rmcResponse := nex.NewRMCResponse(nexproto.Friends3DSProtocolID, callID)
rmcResponse.SetSuccess(nexproto.Friends3DSMethodUpdateMii, nil)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}

28
update_preference.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func updatePreference(err error, client *nex.Client, callID uint32, unknown1 bool, unknown2 bool, unknown3 bool) {
// TODO: Do something with this
rmcResponse := nex.NewRMCResponse(nexproto.Friends3DSProtocolID, callID)
rmcResponse.SetSuccess(nexproto.Friends3DSMethodUpdatePreference, nil)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}

28
update_presence.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func updatePresence(err error, client *nex.Client, callID uint32, presense *nexproto.NintendoPresence, unknown bool) {
// TODO: Do something with this
rmcResponse := nex.NewRMCResponse(nexproto.Friends3DSProtocolID, callID)
rmcResponse.SetSuccess(nexproto.Friends3DSMethodUpdatePresence, nil)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}

28
update_profile.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
nex "github.com/PretendoNetwork/nex-go"
nexproto "github.com/PretendoNetwork/nex-protocols-go"
)
func updateProfile(err error, client *nex.Client, callID uint32, profileData *nexproto.MyProfile) {
// TODO: Do something with this
rmcResponse := nex.NewRMCResponse(nexproto.Friends3DSProtocolID, callID)
rmcResponse.SetSuccess(nexproto.Friends3DSMethodUpdateProfile, nil)
rmcResponseBytes := rmcResponse.Bytes()
responsePacket, _ := nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)
responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)
nexServer.Send(responsePacket)
}