mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-10-02 03:56:34 -05:00
Database: Store and verify NG device ID
This commit is contained in:
@@ -2,30 +2,31 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsbrcd string, profileId uint32) (User, bool) {
|
||||
func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsbrcd string, profileId uint32, ngDeviceId uint32) (User, bool) {
|
||||
var exists bool
|
||||
err := pool.QueryRow(ctx, DoesUserExist, userId, gsbrcd).Scan(&exists)
|
||||
if err != nil {
|
||||
return User{}, false
|
||||
}
|
||||
|
||||
uniqueNickname := common.Base32Encode(userId) + gsbrcd
|
||||
|
||||
user := User{
|
||||
ProfileId: profileId,
|
||||
UserId: userId,
|
||||
GsbrCode: gsbrcd,
|
||||
Email: uniqueNickname + "@nds",
|
||||
UniqueNick: uniqueNickname,
|
||||
UserId: userId,
|
||||
GsbrCode: gsbrcd,
|
||||
}
|
||||
|
||||
if !exists {
|
||||
user.ProfileId = profileId
|
||||
user.NgDeviceId = ngDeviceId
|
||||
user.UniqueNick = common.Base32Encode(userId) + gsbrcd
|
||||
user.Email = user.UniqueNick + "@nds"
|
||||
|
||||
// Create the GPCM account
|
||||
err := user.CreateUser(pool, ctx)
|
||||
if err != nil {
|
||||
@@ -35,9 +36,24 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
|
||||
|
||||
logging.Notice("DATABASE", "Created new GPCM user:", aurora.Cyan(userId), aurora.Cyan(gsbrcd), aurora.Cyan(user.ProfileId))
|
||||
} else {
|
||||
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.Email, &user.UniqueNick, &user.FirstName, &user.LastName)
|
||||
var expectedNgId *uint32
|
||||
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &expectedNgId, &user.Email, &user.UniqueNick, &user.FirstName, &user.LastName)
|
||||
if err != nil {
|
||||
return User{}, false
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if expectedNgId != nil {
|
||||
user.NgDeviceId = *expectedNgId
|
||||
if ngDeviceId != 0 && user.NgDeviceId != ngDeviceId {
|
||||
logging.Error("DATABASE", "NG device ID mismatch for profile", aurora.Cyan(user.ProfileId), "- expected", aurora.Cyan(fmt.Sprintf("%08x", user.NgDeviceId)), "but got", aurora.Cyan(fmt.Sprintf("%08x", ngDeviceId)))
|
||||
return User{}, false
|
||||
}
|
||||
} else if ngDeviceId != 0 {
|
||||
user.NgDeviceId = ngDeviceId
|
||||
_, err := pool.Exec(ctx, UpdateUserNGDeviceID, user.ProfileId, ngDeviceId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if profileId != 0 && user.ProfileId != profileId {
|
||||
|
||||
@@ -9,14 +9,15 @@ import (
|
||||
|
||||
const (
|
||||
InsertUser = `INSERT INTO users (user_id, gsbrcd, password, email, unique_nick) VALUES ($1, $2, $3, $4, $5) RETURNING profile_id`
|
||||
InsertUserWithProfileID = `INSERT INTO users (user_id, gsbrcd, password, email, unique_nick) VALUES ($1, $2, $3, $4, $5)`
|
||||
InsertUserWithProfileID = `INSERT INTO users (user_id, gsbrcd, password, ng_device_id, email, unique_nick) VALUES ($1, $2, $3, $4, $5, $6)`
|
||||
UpdateUserTable = `UPDATE users SET firstname = CASE WHEN $3 THEN $2 ELSE firstname END, lastname = CASE WHEN $5 THEN $4 ELSE lastname END WHERE profile_id = $1 RETURNING user_id, gsbrcd, email, unique_nick, firstname, lastname`
|
||||
UpdateUserProfileID = `UPDATE users SET profile_id = $3 WHERE user_id = $1 AND gsbrcd = $2`
|
||||
UpdateUserNGDeviceID = `UPDATE users SET ng_device_id = $2 WHERE profile_id = $1`
|
||||
GetUser = `SELECT user_id, gsbrcd, email, unique_nick, firstname, lastname FROM users WHERE profile_id = $1`
|
||||
DoesUserExist = `SELECT EXISTS(SELECT 1 FROM users WHERE user_id = $1 AND gsbrcd = $2)`
|
||||
IsProfileIDInUse = `SELECT EXISTS(SELECT 1 FROM users WHERE profile_id = $1)`
|
||||
DeleteUserSession = `DELETE FROM sessions WHERE profile_id = $1`
|
||||
GetUserProfileID = `SELECT profile_id, email, unique_nick, firstname, lastname FROM users WHERE user_id = $1 AND gsbrcd = $2`
|
||||
GetUserProfileID = `SELECT profile_id, ng_device_id, email, unique_nick, firstname, lastname FROM users WHERE user_id = $1 AND gsbrcd = $2`
|
||||
|
||||
GetMKWFriendInfoQuery = `SELECT mariokartwii_friend_info FROM users WHERE profile_id = $1`
|
||||
UpdateMKWFriendInfoQuery = `UPDATE users SET mariokartwii_friend_info = $2 WHERE profile_id = $1`
|
||||
@@ -26,6 +27,7 @@ type User struct {
|
||||
ProfileId uint32
|
||||
UserId uint64
|
||||
GsbrCode string
|
||||
NgDeviceId uint32
|
||||
Email string
|
||||
UniqueNick string
|
||||
FirstName string
|
||||
@@ -56,7 +58,7 @@ func (user *User) CreateUser(pool *pgxpool.Pool, ctx context.Context) error {
|
||||
return ErrProfileIDInUse
|
||||
}
|
||||
|
||||
_, err = pool.Exec(ctx, InsertUserWithProfileID, user.UserId, user.GsbrCode, "", user.Email, user.UniqueNick, user.ProfileId)
|
||||
_, err = pool.Exec(ctx, InsertUserWithProfileID, user.UserId, user.GsbrCode, "", user.NgDeviceId, user.Email, user.UniqueNick, user.ProfileId)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package gpcm
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
@@ -35,10 +36,10 @@ func generateProof(gpcmChallenge, nasChallenge, authToken, clientChallenge strin
|
||||
return generateResponse(clientChallenge, nasChallenge, authToken, gpcmChallenge)
|
||||
}
|
||||
|
||||
func verifySignature(authToken string, signature string) bool {
|
||||
func verifySignature(authToken string, signature string) uint32 {
|
||||
sigBytes, err := common.Base64DwcEncoding.DecodeString(signature)
|
||||
if err != nil || len(sigBytes) != 0x144 {
|
||||
return false
|
||||
return 0
|
||||
}
|
||||
|
||||
ngId := sigBytes[0x000:0x004]
|
||||
@@ -69,7 +70,7 @@ func verifySignature(authToken string, signature string) bool {
|
||||
|
||||
if !verifyECDSA(msPublicKey, msSignature, ngCertBlobHash[:]) {
|
||||
logging.Error("GPCM", "NG cert verify failed")
|
||||
return false
|
||||
return 0
|
||||
}
|
||||
logging.Info("GPCM", "NG cert verified")
|
||||
|
||||
@@ -88,18 +89,18 @@ func verifySignature(authToken string, signature string) bool {
|
||||
|
||||
if !verifyECDSA(ngPublicKey, ngSignature, apCertBlobHash[:]) {
|
||||
logging.Error("GPCM", "AP cert verify failed")
|
||||
return false
|
||||
return 0
|
||||
}
|
||||
logging.Info("GPCM", "AP cert verified")
|
||||
|
||||
authTokenHash := sha1.Sum([]byte(authToken))
|
||||
if !verifyECDSA(apPublicKey, apSignature, authTokenHash[:]) {
|
||||
logging.Error("GPCM", "Auth token signature failed")
|
||||
return false
|
||||
return 0
|
||||
}
|
||||
logging.Notice("GPCM", "Auth token signature verified; NG ID:", aurora.Cyan(fmt.Sprintf("%08x", ngId)))
|
||||
|
||||
return true
|
||||
return binary.BigEndian.Uint32(ngId)
|
||||
}
|
||||
|
||||
func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
@@ -125,7 +126,17 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
}
|
||||
|
||||
signature, exists := command.OtherValues["wwfc_sig"]
|
||||
if !exists || !verifySignature(authToken, signature) {
|
||||
if !exists {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "Missing authentication signature.",
|
||||
Fatal: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var deviceId uint32
|
||||
if deviceId = verifySignature(authToken, signature); deviceId == 0 {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "The authentication signature is invalid.",
|
||||
@@ -170,7 +181,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
}
|
||||
|
||||
// Perform the login with the database.
|
||||
user, ok := database.LoginUserToGPCM(pool, ctx, userId, gsbrcd, cmdProfileId)
|
||||
user, ok := database.LoginUserToGPCM(pool, ctx, userId, gsbrcd, cmdProfileId, deviceId)
|
||||
if !ok {
|
||||
// There was an error logging in to the GP backend.
|
||||
g.replyError(ErrLogin)
|
||||
|
||||
@@ -25,10 +25,11 @@ SET default_table_access_method = heap;
|
||||
--
|
||||
|
||||
CREATE TABLE public.users (
|
||||
profile_id integer NOT NULL,
|
||||
profile_id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
gsbrcd character varying NOT NULL,
|
||||
password character varying NOT NULL,
|
||||
ng_device_id bigint,
|
||||
email character varying NOT NULL,
|
||||
unique_nick character varying NOT NULL,
|
||||
firstname character varying,
|
||||
|
||||
Reference in New Issue
Block a user