feat: Implement token validation from common protocols

We have to do special handling for guest accounts, so the insecure
method is enabled on both TicketGranting and SecureConnection. Alongside
that, we override the RegisterEx function to do the additional
processing required by friends.
This commit is contained in:
Daniel López Guimaraes 2025-04-17 20:44:56 +01:00
parent 5560c1d7f2
commit 66c6e6028a
No known key found for this signature in database
GPG Key ID: 6AC74DE3DEF050E0
7 changed files with 249 additions and 196 deletions

28
go.mod
View File

@ -7,35 +7,33 @@ toolchain go1.23.6
require (
github.com/PretendoNetwork/grpc-go v1.0.2
github.com/PretendoNetwork/nex-go/v2 v2.1.3
github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.2.2
github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.4.0
github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.1
github.com/PretendoNetwork/plogger-go v1.0.4
github.com/PretendoNetwork/plogger-go v1.1.0
github.com/PretendoNetwork/sql-manager v1.0.0
github.com/golang/protobuf v1.5.4
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
google.golang.org/grpc v1.70.0
google.golang.org/grpc v1.71.1
)
replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20240520151616-dc85e6b867a5
require (
github.com/dolthub/maphash v0.1.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/jwalton/go-supportscolor v1.2.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/lxzan/gws v1.8.8 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e // indirect
github.com/superwhiskers/crunch/v3 v3.5.7 // indirect
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b // indirect
google.golang.org/protobuf v1.36.5 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/term v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/protobuf v1.36.6 // indirect
)

76
go.sum
View File

@ -1,15 +1,13 @@
github.com/PretendoNetwork/grpc-go v1.0.2 h1:9TvKmX7dCOANyoHEra1MMYqS1N/RGav66TRG4SHInvo=
github.com/PretendoNetwork/grpc-go v1.0.2/go.mod h1:XZjEsij9lL7HJBNkH6JPbBIkUSq/1rjflvjGdv+DAj0=
github.com/PretendoNetwork/nex-go/v2 v2.1.2 h1:OJFAS6U6VNzZ4YzteKqUEZ5aJMwWIHODeRrLwNbN7nw=
github.com/PretendoNetwork/nex-go/v2 v2.1.2/go.mod h1:3LyJzsv3AataJW8D0binp15Q8ZH22MWTYly1VNtXi64=
github.com/PretendoNetwork/nex-go/v2 v2.1.3 h1:hdi8PbJIWbpr3WOc1fGJ5ssF76kTxFJ3Wnz46WJYvVs=
github.com/PretendoNetwork/nex-go/v2 v2.1.3/go.mod h1:3LyJzsv3AataJW8D0binp15Q8ZH22MWTYly1VNtXi64=
github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.2.2 h1:rBJNZDJ92pa9fU3Og0sanyizJTWnELPoGR0Tjz8zlws=
github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.2.2/go.mod h1:iuNMuBK/zww+44d6ajfLsOusXx/6Llj3zSkmhJwMuuM=
github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.4.0 h1:EhXj1EDbNgdg40BPx/7n1HHsAy/DayGIWthu81UNyvI=
github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.4.0/go.mod h1:tNtZly5sL3wfy4LVgybS2efm00L/wNgyvcrBV59S/YM=
github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.1 h1:/dsuP0W7bZNvrXoXH0ZRdxpxonfbWmmson51WCQdpEQ=
github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.1/go.mod h1:+soBHmwX6ixGxj6cphLuCvfJqxcZPuowc/5e7Qi9Bz0=
github.com/PretendoNetwork/plogger-go v1.0.4 h1:PF7xHw9eDRHH+RsAP9tmAE7fG0N0p6H4iPwHKnsoXwc=
github.com/PretendoNetwork/plogger-go v1.0.4/go.mod h1:7kD6M4vPq1JL4LTuPg6kuB1OvUBOwQOtAvTaUwMbwvU=
github.com/PretendoNetwork/plogger-go v1.1.0 h1:x2XgyeeM8zDFGy+NcIZd3SYC2fNrVWpBBbkqTejOfiM=
github.com/PretendoNetwork/plogger-go v1.1.0/go.mod h1:wpltahp91IXr9nOvWgwep8zGtUKDeCVwm+/Wa484lQ4=
github.com/PretendoNetwork/sql-manager v1.0.0 h1:g0SYpQgi6Kk4ptufrLTSmDxvqaYioTcfXaDH+uXC+a0=
github.com/PretendoNetwork/sql-manager v1.0.0/go.mod h1:NaEdDC0S/9J8eoxCDvuHB8fofv0svh44lWvgCdtuMq0=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -32,8 +30,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jwalton/go-supportscolor v1.2.0 h1:g6Ha4u7Vm3LIsQ5wmeBpS4gazu0UP1DRDE8y6bre4H8=
github.com/jwalton/go-supportscolor v1.2.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lxzan/gws v1.8.8 h1:st193ZG8qN8sSw8/g/UituFhs7etmKzS7jUqhijg5wM=
@ -50,39 +48,41 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/superwhiskers/crunch/v3 v3.5.7 h1:N9RLxaR65C36i26BUIpzPXGy2f6pQ7wisu2bawbKNqg=
github.com/superwhiskers/crunch/v3 v3.5.7/go.mod h1:4ub2EKgF1MAhTjoOCTU4b9uLMsAweHEa89aRrfAypXA=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg=
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b h1:FQtJ1MxbXoIIrZHZ33M+w5+dAP9o86rgpjoKr/ZmT7k=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -3,56 +3,26 @@ package nex_account_management
import (
"crypto/hmac"
"crypto/md5"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"strings"
"github.com/PretendoNetwork/friends/globals"
"github.com/PretendoNetwork/friends/utility"
nex "github.com/PretendoNetwork/nex-go/v2"
"github.com/PretendoNetwork/nex-go/v2/types"
account_management "github.com/PretendoNetwork/nex-protocols-go/v2/account-management"
account_management_types "github.com/PretendoNetwork/nex-protocols-go/v2/account-management/types"
)
func NintendoCreateAccount(err error, packet nex.PacketInterface, callID uint32, strPrincipalName types.String, strKey types.String, uiGroups types.UInt32, strEmail types.String, oAuthData types.DataHolder) (*nex.RMCMessage, *nex.Error) {
if err != nil {
globals.Logger.Error(err.Error())
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "") // TODO - Add error message
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, err.Error())
}
var tokenBase64 string
oAuthDataType := oAuthData.Object.DataObjectID().(types.String)
switch oAuthDataType {
case "NintendoCreateAccountData": // * Wii U
nintendoCreateAccountData := oAuthData.Object.Copy().(account_management_types.NintendoCreateAccountData)
tokenBase64 = string(nintendoCreateAccountData.Token)
case "AccountExtraInfo": // * 3DS
accountExtraInfo := oAuthData.Object.Copy().(account_management_types.AccountExtraInfo)
tokenBase64 = string(accountExtraInfo.NEXToken)
tokenBase64 = strings.Replace(tokenBase64, ".", "+", -1)
tokenBase64 = strings.Replace(tokenBase64, "-", "/", -1)
tokenBase64 = strings.Replace(tokenBase64, "*", "=", -1)
default:
globals.Logger.Errorf("Invalid oAuthData data type %s!", oAuthDataType)
return nil, nex.NewError(nex.ResultCodes.Authentication.TokenParseError, "") // TODO - Add error message
}
encryptedToken, err := base64.StdEncoding.DecodeString(tokenBase64)
if err != nil {
globals.Logger.Error(err.Error())
return nil, nex.NewError(nex.ResultCodes.Authentication.TokenParseError, "") // TODO - Add error message
}
decryptedToken, err := utility.DecryptToken(encryptedToken)
if err != nil {
globals.Logger.Error(err.Error())
return nil, nex.NewError(nex.ResultCodes.Authentication.TokenParseError, "") // TODO - Add error message
decryptedToken, nexError := utility.ValidateNintendoCreateAccountToken(oAuthData)
if nexError != nil {
globals.Logger.Error(nexError.Error())
return nil, nexError
}
pid := types.NewPID(uint64(decryptedToken.UserPID))
@ -64,7 +34,7 @@ func NintendoCreateAccount(err error, packet nex.PacketInterface, callID uint32,
_, err = mac.Write(pidByteArray)
if err != nil {
globals.Logger.Error(err.Error())
return nil, nex.NewError(nex.ResultCodes.Authentication.Unknown, "") // TODO - Add error message
return nil, nex.NewError(nex.ResultCodes.Authentication.Unknown, err.Error())
}
pidHmac := types.NewString(hex.EncodeToString(mac.Sum(nil)))

View File

@ -9,8 +9,12 @@ import (
func registerCommonSecureServerProtocols() {
secureConnectionProtocol := secure_connection.NewProtocol()
common_secure_connection.NewCommonProtocol(secureConnectionProtocol)
commonSecureConnectionProtocol := common_secure_connection.NewCommonProtocol(secureConnectionProtocol)
// * On account creation the console logs in with a guest account and uses the Register method
commonSecureConnectionProtocol.EnableInsecureRegister()
// * Override RegisterEx so that we can register the user into the ConnectedUsers map
secureConnectionProtocol.RegisterEx = nex_secure_connection.RegisterEx
globals.SecureEndpoint.RegisterServiceProtocol(secureConnectionProtocol)

View File

@ -3,26 +3,103 @@ package nex_secure_connection
import (
"net"
"github.com/PretendoNetwork/nex-go/v2"
"github.com/PretendoNetwork/nex-go/v2/constants"
"github.com/PretendoNetwork/nex-go/v2/types"
secure_connection "github.com/PretendoNetwork/nex-protocols-go/v2/secure-connection"
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
database_3ds "github.com/PretendoNetwork/friends/database/3ds"
database_wiiu "github.com/PretendoNetwork/friends/database/wiiu"
"github.com/PretendoNetwork/friends/globals"
friends_types "github.com/PretendoNetwork/friends/types"
nex "github.com/PretendoNetwork/nex-go/v2"
"github.com/PretendoNetwork/nex-go/v2/types"
secure_connection "github.com/PretendoNetwork/nex-protocols-go/v2/secure-connection"
)
func RegisterEx(err error, packet nex.PacketInterface, callID uint32, vecMyURLs types.List[types.StationURL], hCustomData types.DataHolder) (*nex.RMCMessage, *nex.Error) {
if err != nil {
globals.Logger.Error(err.Error())
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "")
common_globals.Logger.Error(err.Error())
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, err.Error())
}
connection := packet.Sender().(*nex.PRUDPConnection)
endpoint := connection.Endpoint()
retval := types.NewQResultSuccess(nex.ResultCodes.Core.Unknown)
var retval types.QResult
pidConnectionID := types.NewUInt32(0)
urlPublic := types.NewString("")
errorCode := common_globals.ValidatePretendoLoginData(connection.PID(), hCustomData, globals.AESKey)
if errorCode != nil {
common_globals.Logger.Error(errorCode.Message)
retval = types.NewQResultError(errorCode.ResultCode)
} else {
// * vecMyURLs may contain multiple StationURLs. Search them all
var localStation *types.StationURL
var publicStation *types.StationURL
for _, stationURL := range vecMyURLs {
natf, ok := stationURL.NATFiltering()
if !ok {
continue
}
natm, ok := stationURL.NATMapping()
if !ok {
continue
}
// * Station reports itself as being non-public (local)
if localStation == nil && !stationURL.IsPublic() {
localStation = &stationURL
}
// * Still did not find the station, trying heuristics
if localStation == nil && natf == constants.UnknownNATFiltering && natm == constants.UnknownNATMapping {
localStation = &stationURL
}
if publicStation == nil && stationURL.IsPublic() {
publicStation = &stationURL
}
}
if localStation == nil {
common_globals.Logger.Error("Failed to find local station")
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
}
if publicStation == nil {
publicStation = localStation
var address string
var port uint16
// * We have to duplicate this because Go automatically breaks on switch statements
switch clientAddress := connection.Address().(type) {
case *net.UDPAddr:
address = clientAddress.IP.String()
port = uint16(clientAddress.Port)
case *net.TCPAddr:
address = clientAddress.IP.String()
port = uint16(clientAddress.Port)
}
publicStation.SetAddress(address)
publicStation.SetPortNumber(port)
publicStation.SetNATFiltering(constants.UnknownNATFiltering)
publicStation.SetNATMapping(constants.UnknownNATMapping)
publicStation.SetType(uint8(constants.StationURLFlagPublic) | uint8(constants.StationURLFlagBehindNAT))
}
localStation.SetPrincipalID(connection.PID())
publicStation.SetPrincipalID(connection.PID())
localStation.SetRVConnectionID(connection.ID)
publicStation.SetRVConnectionID(connection.ID)
connection.StationURLs = append(connection.StationURLs, *localStation)
connection.StationURLs = append(connection.StationURLs, *publicStation)
// TODO - Validate loginData
pid := uint32(connection.PID())
user := friends_types.NewConnectedUser()
@ -54,26 +131,16 @@ func RegisterEx(err error, packet nex.PacketInterface, callID uint32, vecMyURLs
retval = types.NewQResultError(nex.ResultCodes.Authentication.ValidationFailed)
}
pidConnectionID := types.NewUInt32(0)
urlPublic := types.NewString("")
if retval.IsSuccess() {
if !retval.IsError() {
globals.ConnectedUsers.Set(pid, user)
localStation := vecMyURLs[0]
address := connection.Address().(*net.UDPAddr)
localStation.SetAddress(address.IP.String())
localStation.SetPortNumber(uint16(address.Port))
localStationURL := localStation.URL()
retval = types.NewQResultSuccess(nex.ResultCodes.Core.Unknown)
pidConnectionID = types.NewUInt32(connection.ID)
urlPublic = types.NewString(localStationURL)
urlPublic = types.NewString(publicStation.URL())
}
}
rmcResponseStream := nex.NewByteStreamOut(globals.SecureEndpoint.LibraryVersions(), globals.SecureEndpoint.ByteStreamSettings())
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
retval.WriteTo(rmcResponseStream)
pidConnectionID.WriteTo(rmcResponseStream)
@ -81,7 +148,7 @@ func RegisterEx(err error, packet nex.PacketInterface, callID uint32, vecMyURLs
rmcResponseBody := rmcResponseStream.Bytes()
rmcResponse := nex.NewRMCSuccess(globals.SecureEndpoint, rmcResponseBody)
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
rmcResponse.ProtocolID = secure_connection.ProtocolID
rmcResponse.MethodID = secure_connection.MethodRegisterEx
rmcResponse.CallID = callID

70
utility/authentication.go Normal file
View File

@ -0,0 +1,70 @@
package utility
import (
"encoding/base64"
"fmt"
"strings"
"time"
"github.com/PretendoNetwork/nex-go/v2"
"github.com/PretendoNetwork/nex-go/v2/types"
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
account_management_types "github.com/PretendoNetwork/nex-protocols-go/v2/account-management/types"
"github.com/PretendoNetwork/friends/globals"
)
// ValidateNintendoCreateAccountToken validates the given Pretendo token for account creation
func ValidateNintendoCreateAccountToken(token types.DataHolder) (*common_globals.NEXToken, *nex.Error) {
var tokenBase64 string
tokenDataType := token.Object.DataObjectID().(types.String)
switch tokenDataType {
case "NintendoCreateAccountData": // * Wii U
nintendoCreateAccountData := token.Object.Copy().(account_management_types.NintendoCreateAccountData)
tokenBase64 = string(nintendoCreateAccountData.Token)
case "AccountExtraInfo": // * 3DS
accountExtraInfo := token.Object.Copy().(account_management_types.AccountExtraInfo)
tokenBase64 = string(accountExtraInfo.NEXToken)
tokenBase64 = strings.Replace(tokenBase64, ".", "+", -1)
tokenBase64 = strings.Replace(tokenBase64, "-", "/", -1)
tokenBase64 = strings.Replace(tokenBase64, "*", "=", -1)
default:
globals.Logger.Errorf("Invalid token data type %s!", tokenDataType)
return nil, nex.NewError(nex.ResultCodes.Authentication.ValidationFailed, fmt.Sprintf("Invalid token data type %s!", tokenDataType))
}
encryptedToken, err := base64.StdEncoding.DecodeString(tokenBase64)
if err != nil {
globals.Logger.Error(err.Error())
return nil, nex.NewError(nex.ResultCodes.Authentication.ValidationFailed, err.Error())
}
decryptedToken, nexError := common_globals.DecryptToken(encryptedToken, globals.AESKey)
if nexError != nil {
return nil, nexError
}
// Check for NEX token type
if decryptedToken.TokenType != 3 {
return nil, nex.NewError(nex.ResultCodes.Authentication.ValidationFailed, "Invalid token type")
}
// Expire time is in milliseconds
expireTime := time.Unix(int64(decryptedToken.ExpireTime / 1000), 0)
if expireTime.Before(time.Now()) {
return nil, nex.NewError(nex.ResultCodes.Authentication.TokenExpired, "Token expired")
}
// PID isn't checked since account creation is done with a guest account
if decryptedToken.AccessLevel < 0 {
return nil, nex.NewError(nex.ResultCodes.RendezVous.AccountDisabled, fmt.Sprintf("Account %d is banned", decryptedToken.UserPID))
}
return decryptedToken, nil
}

View File

@ -1,56 +0,0 @@
package utility
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"github.com/PretendoNetwork/friends/globals"
"github.com/PretendoNetwork/friends/types"
)
func DecryptToken(encryptedToken []byte) (*types.NEXToken, error) {
// Decrypt the token body
block, err := aes.NewCipher(globals.AESKey)
if err != nil {
return nil, err
}
expectedChecksum := binary.BigEndian.Uint32(encryptedToken[0:4])
encryptedBody := encryptedToken[4:]
decrypted := make([]byte, len(encryptedBody))
iv := make([]byte, 16)
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(decrypted, encryptedBody)
paddingSize := int(decrypted[len(decrypted)-1])
if paddingSize < 0 || paddingSize >= len(decrypted) {
return nil, fmt.Errorf("Invalid padding size %d for token %x", paddingSize, encryptedToken)
}
decrypted = decrypted[:len(decrypted)-paddingSize]
table := crc32.MakeTable(crc32.IEEE)
calculatedChecksum := crc32.Checksum(decrypted, table)
if expectedChecksum != calculatedChecksum {
return nil, errors.New("Checksum did not match. Failed decrypt. Are you using the right key?")
}
// Unpack the token body to struct
token := &types.NEXToken{}
tokenReader := bytes.NewBuffer(decrypted)
err = binary.Read(tokenReader, binary.LittleEndian, token)
if err != nil {
return nil, err
}
return token, nil
}