mirror of
https://github.com/PretendoNetwork/friends.git
synced 2026-10-02 14:58:05 -05:00
3DS friends goes online
This commit is contained in:
23
crypto.go
23
crypto.go
@@ -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
16
main.go
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
69
nintendo_create_account_3ds.go
Normal file
69
nintendo_create_account_3ds.go
Normal 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
140
sig.go
Normal 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
34
sync_friend.go
Normal 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
28
update_comment.go
Normal 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)
|
||||
}
|
||||
28
update_favorite_game_key.go
Normal file
28
update_favorite_game_key.go
Normal 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
28
update_mii.go
Normal 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
28
update_preference.go
Normal 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
28
update_presence.go
Normal 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
28
update_profile.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user