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