mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-09-30 11:06:34 -05:00
GPCM: Verify new auth token signature
This commit is contained in:
272
gpcm/ecdsa.go
Normal file
272
gpcm/ecdsa.go
Normal file
@@ -0,0 +1,272 @@
|
||||
// Based on segher's wii.git "ec.c"
|
||||
// Copyright 2007,2008 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
|
||||
package gpcm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
var (
|
||||
curveN = new(big.Int).SetBytes([]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xe9, 0x74, 0xe7, 0x2f, 0x8a, 0x69, 0x22, 0x03, 0x1d, 0x26, 0x03, 0xcf, 0xe0, 0xd7})
|
||||
curveGBytes = []byte{0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1, 0xbb, 0x75, 0x5f, 0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8, 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, 0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19, 0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, 0x28, 0xbe, 0xbf, 0x8a, 0x0b, 0xef, 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52}
|
||||
square = []byte{0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15, 0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55}
|
||||
)
|
||||
|
||||
func eltIsZero(d []byte) bool {
|
||||
for i := 0; i < 30; i++ {
|
||||
if d[i] != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func eltAdd(a []byte, b []byte) []byte {
|
||||
d := make([]byte, 30)
|
||||
for i := 0; i < 30; i++ {
|
||||
d[i] = a[i] ^ b[i]
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func eltMulX(d []byte, a []byte) {
|
||||
carry := a[0] & 1
|
||||
|
||||
x := byte(0)
|
||||
for i := 0; i < 29; i++ {
|
||||
y := a[i+1]
|
||||
d[i] = x ^ (y >> 7)
|
||||
x = y << 1
|
||||
}
|
||||
d[29] = x ^ carry
|
||||
d[20] ^= carry << 2
|
||||
}
|
||||
|
||||
func eltMul(a []byte, b []byte) []byte {
|
||||
d := make([]byte, 30)
|
||||
|
||||
i := 0
|
||||
mask := byte(1)
|
||||
for n := 0; n < 233; n++ {
|
||||
eltMulX(d, d)
|
||||
|
||||
if (a[i] & mask) != 0 {
|
||||
d = eltAdd(d, b)
|
||||
}
|
||||
|
||||
mask >>= 1
|
||||
if mask == 0 {
|
||||
mask = 0x80
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func eltSquareToWide(a []byte) []byte {
|
||||
d := make([]byte, 60)
|
||||
for i := 0; i < 30; i++ {
|
||||
d[2*i] = square[a[i]>>4]
|
||||
d[2*i+1] = square[a[i]&15]
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func wideReduce(d []byte) {
|
||||
for i := 0; i < 30; i++ {
|
||||
x := d[i]
|
||||
|
||||
d[i+19] ^= x >> 7
|
||||
d[i+20] ^= x << 1
|
||||
|
||||
d[i+29] ^= x >> 1
|
||||
d[i+30] ^= x << 7
|
||||
}
|
||||
|
||||
x := d[30] & 0xfe
|
||||
|
||||
d[49] ^= x >> 7
|
||||
d[50] ^= x << 1
|
||||
|
||||
d[59] ^= x >> 1
|
||||
|
||||
d[30] &= 1
|
||||
}
|
||||
|
||||
func eltSquare(a []byte) []byte {
|
||||
wide := eltSquareToWide(a)
|
||||
wideReduce(wide)
|
||||
return wide[30:]
|
||||
}
|
||||
|
||||
func itohTsujii(a []byte, b []byte, j uint32) []byte {
|
||||
t := bytes.Clone(a)
|
||||
for ; j != 0; j-- {
|
||||
t = eltSquare(t)
|
||||
}
|
||||
|
||||
return eltMul(t, b)
|
||||
}
|
||||
|
||||
func eltInv(a []byte) []byte {
|
||||
|
||||
t := itohTsujii(a, a, 1)
|
||||
s := itohTsujii(t, a, 1)
|
||||
t = itohTsujii(s, s, 3)
|
||||
s = itohTsujii(t, a, 1)
|
||||
t = itohTsujii(s, s, 7)
|
||||
s = itohTsujii(t, t, 14)
|
||||
t = itohTsujii(s, a, 1)
|
||||
s = itohTsujii(t, t, 29)
|
||||
t = itohTsujii(s, s, 58)
|
||||
s = itohTsujii(t, t, 116)
|
||||
return eltSquare(s)
|
||||
}
|
||||
|
||||
func pointIsZero(p []byte) bool {
|
||||
for i := 0; i < 60; i++ {
|
||||
if p[i] != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func pointDouble(p []byte) []byte {
|
||||
px := p[:30]
|
||||
py := p[30:]
|
||||
|
||||
if eltIsZero(px) {
|
||||
return make([]byte, 60)
|
||||
}
|
||||
|
||||
t := eltInv(px)
|
||||
s := eltMul(py, t)
|
||||
s = eltAdd(s, px)
|
||||
|
||||
t = eltSquare(px)
|
||||
|
||||
rx := eltSquare(s)
|
||||
rx = eltAdd(rx, s)
|
||||
rx[29] ^= 1
|
||||
|
||||
ry := eltMul(s, rx)
|
||||
ry = eltAdd(ry, rx)
|
||||
ry = eltAdd(ry, t)
|
||||
|
||||
return append(rx, ry...)
|
||||
}
|
||||
|
||||
func pointAdd(p []byte, q []byte) []byte {
|
||||
if pointIsZero(p) {
|
||||
return bytes.Clone(q)
|
||||
}
|
||||
|
||||
if pointIsZero(q) {
|
||||
return bytes.Clone(p)
|
||||
}
|
||||
|
||||
px := p[:30]
|
||||
py := p[30:]
|
||||
qx := q[:30]
|
||||
qy := q[30:]
|
||||
|
||||
u := eltAdd(px, qx)
|
||||
|
||||
if eltIsZero(u) {
|
||||
u = eltAdd(py, qy)
|
||||
if eltIsZero(u) {
|
||||
return pointDouble(p)
|
||||
}
|
||||
return make([]byte, 60)
|
||||
}
|
||||
|
||||
t := eltInv(u)
|
||||
u = eltAdd(py, qy)
|
||||
s := eltMul(t, u)
|
||||
|
||||
t = eltSquare(s)
|
||||
t = eltAdd(t, s)
|
||||
t = eltAdd(t, qx)
|
||||
t[29] ^= 1
|
||||
|
||||
u = eltMul(s, t)
|
||||
s = eltAdd(u, py)
|
||||
rx := eltAdd(t, px)
|
||||
ry := eltAdd(s, rx)
|
||||
|
||||
return append(rx, ry...)
|
||||
}
|
||||
|
||||
func pointMul(a []byte, b []byte) []byte {
|
||||
d := make([]byte, 60)
|
||||
|
||||
for i := 0; i < 30; i++ {
|
||||
for mask := byte(0x80); mask != 0; mask >>= 1 {
|
||||
d = pointDouble(d)
|
||||
if (a[i] & mask) != 0 {
|
||||
d = pointAdd(d, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func bigIntToBytes(a *big.Int) []byte {
|
||||
r := a.Bytes()
|
||||
if len(r) < 30 {
|
||||
return append(make([]byte, 30-len(r)), r...)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func printHex(data []byte) {
|
||||
logMsg := ""
|
||||
for i := 0; i < len(data); i++ {
|
||||
if (i % 16) == 0 {
|
||||
logMsg += "\n"
|
||||
}
|
||||
logMsg += fmt.Sprintf("%02x ", data[i])
|
||||
}
|
||||
logging.Info("GPCM", "Data:", logMsg)
|
||||
}
|
||||
|
||||
func verifyECDSA(publicKey []byte, signature []byte, hash []byte) bool {
|
||||
r := big.NewInt(0).SetBytes(signature[0x00:0x1E])
|
||||
s := big.NewInt(0).SetBytes(signature[0x1E:0x3C])
|
||||
|
||||
inv := big.NewInt(0).ModInverse(s, curveN)
|
||||
e := big.NewInt(0).SetBytes(hash)
|
||||
|
||||
// printHex(bigIntToBytes(inv))
|
||||
|
||||
w1 := big.NewInt(0).Mul(e, inv)
|
||||
w1.Mod(w1, curveN)
|
||||
w2 := big.NewInt(0).Mul(r, inv)
|
||||
w2.Mod(w2, curveN)
|
||||
|
||||
// printHex(bigIntToBytes(w1))
|
||||
// printHex(bigIntToBytes(w2))
|
||||
|
||||
r1 := pointMul(bigIntToBytes(w1), curveGBytes)
|
||||
r2 := pointMul(bigIntToBytes(w2), publicKey)
|
||||
r3 := pointAdd(r1, r2)
|
||||
rx := big.NewInt(0).SetBytes(r3[:30])
|
||||
|
||||
// printHex(r1)
|
||||
// printHex(r2)
|
||||
// printHex(bigIntToBytes(rx))
|
||||
|
||||
if rx.Cmp(curveN) >= 0 {
|
||||
// TODO: This is correct right?
|
||||
rx.Sub(rx, curveN)
|
||||
rx.Mod(rx, curveN)
|
||||
}
|
||||
|
||||
return rx.Cmp(r) == 0
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package gpcm
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
@@ -30,6 +32,73 @@ func generateProof(gpcmChallenge, nasChallenge, authToken, clientChallenge strin
|
||||
return generateResponse(clientChallenge, nasChallenge, authToken, gpcmChallenge)
|
||||
}
|
||||
|
||||
func verifySignature(authToken string, signature string) bool {
|
||||
sigBytes, err := common.Base64DwcEncoding.DecodeString(signature)
|
||||
if err != nil || len(sigBytes) != 0x144 {
|
||||
return false
|
||||
}
|
||||
|
||||
ngId := sigBytes[0x000:0x004]
|
||||
ngTimestamp := sigBytes[0x004:0x008]
|
||||
caId := sigBytes[0x008:0x00C]
|
||||
msId := sigBytes[0x00C:0x010]
|
||||
apId := sigBytes[0x010:0x018]
|
||||
msSignature := sigBytes[0x018:0x054]
|
||||
ngPublicKey := sigBytes[0x054:0x090]
|
||||
ngSignature := sigBytes[0x090:0x0CC]
|
||||
apPublicKey := sigBytes[0x0CC:0x108]
|
||||
apSignature := sigBytes[0x108:0x144]
|
||||
|
||||
ngIssuer := fmt.Sprintf("Root-CA%02x%02x%02x%02x-MS%02x%02x%02x%02x", caId[0], caId[1], caId[2], caId[3], msId[0], msId[1], msId[2], msId[3])
|
||||
ngName := fmt.Sprintf("NG%02x%02x%02x%02x", ngId[0], ngId[1], ngId[2], ngId[3])
|
||||
|
||||
ngCertBlob := []byte(ngIssuer)
|
||||
ngCertBlob = append(ngCertBlob, make([]byte, 0x40-len(ngIssuer))...)
|
||||
ngCertBlob = append(ngCertBlob, 0x00, 0x00, 0x00, 0x02)
|
||||
ngCertBlob = append(ngCertBlob, []byte(ngName)...)
|
||||
ngCertBlob = append(ngCertBlob, make([]byte, 0x40-len(ngName))...)
|
||||
ngCertBlob = append(ngCertBlob, ngTimestamp...)
|
||||
ngCertBlob = append(ngCertBlob, ngPublicKey...)
|
||||
ngCertBlob = append(ngCertBlob, make([]byte, 0x3C)...)
|
||||
ngCertBlobHash := sha1.Sum(ngCertBlob)
|
||||
|
||||
msPublicKey := []byte{0x00, 0xFD, 0x56, 0x04, 0x18, 0x2C, 0xF1, 0x75, 0x09, 0x21, 0x00, 0xC3, 0x08, 0xAE, 0x48, 0x39, 0x91, 0x1B, 0x6F, 0x9F, 0xA1, 0xD5, 0x3A, 0x95, 0xAF, 0x08, 0x33, 0x49, 0x47, 0x2B, 0x00, 0x01, 0x71, 0x31, 0x69, 0xB5, 0x91, 0xFF, 0xD3, 0x0C, 0xBF, 0x73, 0xDA, 0x76, 0x64, 0xBA, 0x8D, 0x0D, 0xF9, 0x5B, 0x4D, 0x11, 0x04, 0x44, 0x64, 0x35, 0xC0, 0xED, 0xA4, 0x2F}
|
||||
|
||||
if !verifyECDSA(msPublicKey, msSignature, ngCertBlobHash[:]) {
|
||||
logging.Error("GPCM", "NG cert verify failed")
|
||||
return false
|
||||
}
|
||||
logging.Info("GPCM", "NG cert verified")
|
||||
|
||||
apIssuer := ngIssuer + "-" + ngName
|
||||
apName := fmt.Sprintf("AP%02x%02x%02x%02x%02x%02x%02x%02x", apId[0], apId[1], apId[2], apId[3], apId[4], apId[5], apId[6], apId[7])
|
||||
|
||||
apCertBlob := []byte(apIssuer)
|
||||
apCertBlob = append(apCertBlob, make([]byte, 0x40-len(apIssuer))...)
|
||||
apCertBlob = append(apCertBlob, 0x00, 0x00, 0x00, 0x02)
|
||||
apCertBlob = append(apCertBlob, []byte(apName)...)
|
||||
apCertBlob = append(apCertBlob, make([]byte, 0x40-len(apName))...)
|
||||
apCertBlob = append(apCertBlob, 0x00, 0x00, 0x00, 0x00)
|
||||
apCertBlob = append(apCertBlob, apPublicKey...)
|
||||
apCertBlob = append(apCertBlob, make([]byte, 0x3C)...)
|
||||
apCertBlobHash := sha1.Sum(apCertBlob)
|
||||
|
||||
if !verifyECDSA(ngPublicKey, ngSignature, apCertBlobHash[:]) {
|
||||
logging.Error("GPCM", "AP cert verify failed")
|
||||
return false
|
||||
}
|
||||
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
|
||||
}
|
||||
logging.Notice("GPCM", "Auth token signature verified")
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
if g.LoggedIn {
|
||||
logging.Error(g.ModuleName, "Attempt to login twice")
|
||||
@@ -47,6 +116,24 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
}
|
||||
|
||||
authToken := command.OtherValues["authtoken"]
|
||||
if authToken == "" {
|
||||
g.replyError(ErrLogin)
|
||||
return
|
||||
}
|
||||
|
||||
signature, exists := command.OtherValues["wwfc_auth_sig"]
|
||||
if exists {
|
||||
// TODO: This is still in testing so it's only checked if it exists
|
||||
if !verifySignature(authToken, signature) {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "The authentication signature is invalid.",
|
||||
Fatal: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
challenge := database.GetChallenge(pool, ctx, authToken)
|
||||
if challenge == "" {
|
||||
g.replyError(ErrLogin)
|
||||
@@ -75,7 +162,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
|
||||
// Check to see if a session is already open with this profile ID
|
||||
mutex.Lock()
|
||||
_, exists := sessions[g.User.ProfileId]
|
||||
_, exists = sessions[g.User.ProfileId]
|
||||
if exists {
|
||||
mutex.Unlock()
|
||||
// Original GPCM would've force kicked the other logged in client,
|
||||
|
||||
Reference in New Issue
Block a user