mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-05-06 13:40:04 -05:00
171 lines
3.9 KiB
Go
171 lines
3.9 KiB
Go
package common
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func generateRandom(n int) []byte {
|
|
key := make([]byte, n)
|
|
|
|
read, err := rand.Read(key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if read != n {
|
|
panic("short rand.Read()")
|
|
}
|
|
|
|
return key
|
|
}
|
|
|
|
var (
|
|
authTokenKey = generateRandom(16)
|
|
authTokenIV = generateRandom(16)
|
|
authTokenMagic = generateRandom(8)
|
|
|
|
loginTicketKey = generateRandom(16)
|
|
loginTicketIV = generateRandom(16)
|
|
loginTicketMagic = generateRandom(4)
|
|
)
|
|
|
|
var (
|
|
ErrTokenMagic = errors.New("invalid auth token or login ticket magic")
|
|
ErrTokenExpired = errors.New("auth token or login ticket expired")
|
|
ErrTokenLength = errors.New("invalid auth token or login ticket length")
|
|
)
|
|
|
|
type NASAuthToken struct {
|
|
IssueTime uint64
|
|
UserID uint64
|
|
ConsoleFriendCode uint64
|
|
Region byte
|
|
Lang byte
|
|
UnitCode byte
|
|
GameCode [4]byte
|
|
GsbrCode [16]byte
|
|
Challenge [8]byte
|
|
InGameScreenName [64]byte
|
|
Magic [8]byte
|
|
}
|
|
|
|
var nasAuthTokenSize = (binary.Size(NASAuthToken{}) + aes.BlockSize - 1) & ^(aes.BlockSize - 1)
|
|
|
|
func (t NASAuthToken) Marshal() string {
|
|
t.IssueTime = uint64(time.Now().UTC().Unix())
|
|
copy(t.Magic[:], authTokenMagic)
|
|
|
|
var buf bytes.Buffer
|
|
ShouldNotError(binary.Write(&buf, binary.LittleEndian, t))
|
|
|
|
// Pad to CBC block size
|
|
data := append(buf.Bytes(), make([]byte, nasAuthTokenSize-len(buf.Bytes()))...)
|
|
|
|
block, err := aes.NewCipher(authTokenKey)
|
|
ShouldNotError(err)
|
|
cipher.NewCBCEncrypter(block, authTokenIV).CryptBlocks(data, data)
|
|
|
|
return "NDS" + Base64DwcEncoding.EncodeToString(data)
|
|
}
|
|
|
|
func (t *NASAuthToken) Unmarshal(data string) error {
|
|
if !strings.HasPrefix(data, "NDS") {
|
|
return ErrTokenLength
|
|
}
|
|
|
|
blob, err := Base64DwcEncoding.DecodeString(data[3:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(blob) != nasAuthTokenSize {
|
|
return ErrTokenLength
|
|
}
|
|
|
|
block, err := aes.NewCipher(authTokenKey)
|
|
ShouldNotError(err)
|
|
cipher.NewCBCDecrypter(block, authTokenIV).CryptBlocks(blob, blob)
|
|
|
|
reader := bytes.NewReader(blob)
|
|
if err := binary.Read(reader, binary.LittleEndian, t); err != nil {
|
|
return err
|
|
}
|
|
|
|
if !bytes.Equal(t.Magic[:], authTokenMagic) {
|
|
return ErrTokenMagic
|
|
}
|
|
|
|
currentTime := time.Now().UTC()
|
|
issueTime := time.Unix(int64(t.IssueTime), 0)
|
|
if issueTime.Before(currentTime.Add(-10*time.Minute)) || issueTime.After(currentTime) {
|
|
return ErrTokenExpired
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type GPCMLoginTicket struct {
|
|
IssueTime uint64
|
|
ProfileID uint32
|
|
Magic [4]byte
|
|
}
|
|
|
|
var gpcmLoginTicketSize = (binary.Size(GPCMLoginTicket{}) + aes.BlockSize - 1) & ^(aes.BlockSize - 1)
|
|
|
|
func (t GPCMLoginTicket) Marshal() string {
|
|
t.IssueTime = uint64(time.Now().UTC().Unix())
|
|
copy(t.Magic[:], loginTicketMagic)
|
|
|
|
var buf bytes.Buffer
|
|
ShouldNotError(binary.Write(&buf, binary.LittleEndian, t))
|
|
|
|
// Pad to CBC block size
|
|
data := append(buf.Bytes(), make([]byte, gpcmLoginTicketSize-len(buf.Bytes()))...)
|
|
|
|
block, err := aes.NewCipher(loginTicketKey)
|
|
ShouldNotError(err)
|
|
cipher.NewCBCEncrypter(block, loginTicketIV).CryptBlocks(data, data)
|
|
|
|
return base64.StdEncoding.EncodeToString(data)
|
|
}
|
|
|
|
func (t *GPCMLoginTicket) Unmarshal(ticket string) error {
|
|
blob, err := base64.StdEncoding.DecodeString(ticket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(blob) != gpcmLoginTicketSize {
|
|
return ErrTokenLength
|
|
}
|
|
|
|
block, err := aes.NewCipher(loginTicketKey)
|
|
ShouldNotError(err)
|
|
cipher.NewCBCDecrypter(block, loginTicketIV).CryptBlocks(blob, blob)
|
|
|
|
reader := bytes.NewReader(blob)
|
|
if err := binary.Read(reader, binary.LittleEndian, t); err != nil {
|
|
return err
|
|
}
|
|
|
|
if !bytes.Equal(t.Magic[:], loginTicketMagic) {
|
|
return ErrTokenMagic
|
|
}
|
|
|
|
currentTime := time.Now().UTC()
|
|
issueTime := time.Unix(int64(t.IssueTime), 0)
|
|
if issueTime.Before(currentTime.Add(-48*time.Hour)) || issueTime.After(currentTime) {
|
|
return ErrTokenExpired
|
|
}
|
|
|
|
return nil
|
|
}
|