]> git.lizzy.rs Git - hydra-dragonfire.git/blob - auth.go
Fix links in doc
[hydra-dragonfire.git] / auth.go
1 package main
2
3 import (
4         "github.com/HimbeerserverDE/srp"
5         "github.com/anon55555/mt"
6         "github.com/dragonfireclient/hydra-dragonfire/convert"
7         "github.com/yuin/gopher-lua"
8         "strings"
9         "time"
10 )
11
12 type authState uint8
13
14 const (
15         asInit authState = iota
16         asRequested
17         asVerified
18         asActive
19         asError
20 )
21
22 type Auth struct {
23         client            *Client
24         username          string
25         password          string
26         language          string
27         version           string
28         state             authState
29         err               string
30         srpBytesA, bytesA []byte
31         userdata          *lua.LUserData
32 }
33
34 var authFuncs = map[string]lua.LGFunction{
35         "username": l_auth_username,
36         "password": l_auth_password,
37         "language": l_auth_language,
38         "version":  l_auth_version,
39         "state":    l_auth_state,
40 }
41
42 func getAuth(l *lua.LState) *Auth {
43         return l.CheckUserData(1).Value.(*Auth)
44 }
45
46 func (auth *Auth) create(client *Client, l *lua.LState) {
47         if client.state != csNew {
48                 panic("can't add auth component after connect")
49         }
50
51         auth.client = client
52         auth.language = "en_US"
53         auth.version = "hydra-dragonfire"
54         auth.state = asInit
55         auth.userdata = l.NewUserData()
56         auth.userdata.Value = auth
57         l.SetMetatable(auth.userdata, l.GetTypeMetatable("hydra.auth"))
58 }
59
60 func (auth *Auth) push() lua.LValue {
61         return auth.userdata
62 }
63
64 func (auth *Auth) connect() {
65         if auth.username == "" {
66                 panic("missing username")
67         }
68
69         go func() {
70                 for auth.client.state == csConnected && auth.state == asInit {
71                         auth.client.conn.SendCmd(&mt.ToSrvInit{
72                                 SerializeVer: serializeVer,
73                                 MinProtoVer:  protoVer,
74                                 MaxProtoVer:  protoVer,
75                                 PlayerName:   auth.username,
76                         })
77                         time.Sleep(500 * time.Millisecond)
78                 }
79         }()
80 }
81
82 func (auth *Auth) fail(err string) {
83         auth.err = err
84         auth.state = asError
85         auth.client.closeConn()
86 }
87
88 func (auth *Auth) checkState(state authState, pkt *mt.Pkt) bool {
89         if auth.state == state {
90                 return true
91         }
92
93         auth.fail("received " + string(convert.PushPktType(pkt)) + " in invalid state")
94         return false
95 }
96
97 func (auth *Auth) process(pkt *mt.Pkt) {
98         if auth.state == asError {
99                 return
100         }
101
102         switch cmd := pkt.Cmd.(type) {
103         case *mt.ToCltHello:
104                 if !auth.checkState(asInit, pkt) {
105                         return
106                 }
107
108                 if cmd.SerializeVer != 28 {
109                         auth.fail("unsupported serialize version")
110                         return
111                 }
112
113                 if cmd.AuthMethods == mt.FirstSRP {
114                         salt, verifier, err := srp.NewClient([]byte(strings.ToLower(auth.username)), []byte(auth.password))
115                         if err != nil {
116                                 auth.fail(err.Error())
117                                 return
118                         }
119
120                         auth.client.conn.SendCmd(&mt.ToSrvFirstSRP{
121                                 Salt:        salt,
122                                 Verifier:    verifier,
123                                 EmptyPasswd: auth.password == "",
124                         })
125                         auth.state = asVerified
126                 } else if cmd.AuthMethods == mt.SRP {
127                         var err error
128                         auth.srpBytesA, auth.bytesA, err = srp.InitiateHandshake()
129                         if err != nil {
130                                 auth.fail(err.Error())
131                                 return
132                         }
133
134                         auth.client.conn.SendCmd(&mt.ToSrvSRPBytesA{
135                                 A:      auth.srpBytesA,
136                                 NoSHA1: true,
137                         })
138                         auth.state = asRequested
139                 } else {
140                         auth.fail("invalid auth methods")
141                         return
142                 }
143
144         case *mt.ToCltSRPBytesSaltB:
145                 if !auth.checkState(asRequested, pkt) {
146                         return
147                 }
148
149                 srpBytesK, err := srp.CompleteHandshake(auth.srpBytesA, auth.bytesA, []byte(strings.ToLower(auth.username)), []byte(auth.password), cmd.Salt, cmd.B)
150                 if err != nil {
151                         auth.fail(err.Error())
152                         return
153                 }
154
155                 M := srp.ClientProof([]byte(auth.username), cmd.Salt, auth.srpBytesA, cmd.B, srpBytesK)
156                 auth.srpBytesA = []byte{}
157                 auth.bytesA = []byte{}
158
159                 if M == nil {
160                         auth.fail("srp safety check fail")
161                         return
162                 }
163
164                 auth.client.conn.SendCmd(&mt.ToSrvSRPBytesM{
165                         M: M,
166                 })
167                 auth.state = asVerified
168
169         case *mt.ToCltAcceptAuth:
170                 auth.client.conn.SendCmd(&mt.ToSrvInit2{Lang: auth.language})
171
172         case *mt.ToCltTimeOfDay:
173                 if auth.state == asActive {
174                         return
175                 }
176
177                 if !auth.checkState(asVerified, pkt) {
178                         return
179                 }
180
181                 auth.client.conn.SendCmd(&mt.ToSrvCltReady{
182                         Major:    5,
183                         Minor:    6,
184                         Patch:    0,
185                         Reserved: 0,
186                         Formspec: 4,
187                         Version:  auth.version,
188                 })
189                 auth.state = asActive
190         }
191 }
192
193 func (auth *Auth) accessProperty(l *lua.LState, key string, ptr *string) int {
194         if str, ok := l.Get(2).(lua.LString); ok {
195                 if auth.client.state != csNew {
196                         panic("can't change " + key + " after connecting")
197                 }
198                 *ptr = string(str)
199                 return 0
200         } else {
201                 l.Push(lua.LString(*ptr))
202                 return 1
203         }
204 }
205
206 func l_auth_username(l *lua.LState) int {
207         auth := getAuth(l)
208         return auth.accessProperty(l, "username", &auth.username)
209 }
210
211 func l_auth_password(l *lua.LState) int {
212         auth := getAuth(l)
213         return auth.accessProperty(l, "password", &auth.password)
214 }
215
216 func l_auth_language(l *lua.LState) int {
217         auth := getAuth(l)
218         return auth.accessProperty(l, "language", &auth.language)
219 }
220
221 func l_auth_version(l *lua.LState) int {
222         auth := getAuth(l)
223         return auth.accessProperty(l, "version", &auth.version)
224 }
225
226 func l_auth_state(l *lua.LState) int {
227         auth := getAuth(l)
228
229         switch auth.state {
230         case asInit:
231                 l.Push(lua.LString("init"))
232         case asRequested:
233                 l.Push(lua.LString("requested"))
234         case asVerified:
235                 l.Push(lua.LString("verified"))
236         case asActive:
237                 l.Push(lua.LString("active"))
238         case asError:
239                 l.Push(lua.LString("error"))
240                 l.Push(lua.LString(auth.err))
241                 return 2
242         }
243
244         return 1
245 }