]> git.lizzy.rs Git - hydra-dragonfire.git/blob - comp_auth.go
Merge pull request #4 from Minetest-j45/master
[hydra-dragonfire.git] / comp_auth.go
1 package main
2
3 import (
4         "github.com/HimbeerserverDE/srp"
5         "github.com/dragonfireclient/hydra-dragonfire/convert"
6         "github.com/dragonfireclient/mt"
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 CompAuth 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 compAuthFuncs = map[string]lua.LGFunction{
35         "username": l_comp_auth_username,
36         "password": l_comp_auth_password,
37         "language": l_comp_auth_language,
38         "version":  l_comp_auth_version,
39         "state":    l_comp_auth_state,
40 }
41
42 func getCompAuth(l *lua.LState) *CompAuth {
43         return l.CheckUserData(1).Value.(*CompAuth)
44 }
45
46 func (comp *CompAuth) create(client *Client, l *lua.LState) {
47         if client.state != csNew {
48                 panic("can't add auth component after connect")
49         }
50
51         comp.client = client
52         comp.language = "en_US"
53         comp.version = "hydra-dragonfire"
54         comp.state = asInit
55         comp.userdata = l.NewUserData()
56         comp.userdata.Value = comp
57         l.SetMetatable(comp.userdata, l.GetTypeMetatable("hydra.comp.auth"))
58 }
59
60 func (comp *CompAuth) push() lua.LValue {
61         return comp.userdata
62 }
63
64 func (comp *CompAuth) connect() {
65         if comp.username == "" {
66                 panic("missing username")
67         }
68
69         go func() {
70                 for comp.client.state == csConnected && comp.state == asInit {
71                         comp.client.conn.SendCmd(&mt.ToSrvInit{
72                                 SerializeVer: serializeVer,
73                                 MinProtoVer:  protoVer,
74                                 MaxProtoVer:  protoVer,
75                                 PlayerName:   comp.username,
76                         })
77                         time.Sleep(500 * time.Millisecond)
78                 }
79         }()
80 }
81
82 func (comp *CompAuth) fail(err string) {
83         comp.err = err
84         comp.state = asError
85         comp.client.closeConn()
86 }
87
88 func (comp *CompAuth) checkState(state authState, pkt *mt.Pkt) bool {
89         if comp.state == state {
90                 return true
91         }
92
93         comp.fail("received " + string(convert.PushPktType(pkt)) + " in invalid state")
94         return false
95 }
96
97 func (comp *CompAuth) process(pkt *mt.Pkt) {
98         if comp.state == asError {
99                 return
100         }
101
102         switch cmd := pkt.Cmd.(type) {
103         case *mt.ToCltHello:
104                 if !comp.checkState(asInit, pkt) {
105                         return
106                 }
107
108                 if cmd.SerializeVer != serializeVer {
109                         comp.fail("unsupported serialize version")
110                         return
111                 }
112
113                 if cmd.AuthMethods == mt.FirstSRP {
114                         salt, verifier, err := srp.NewClient([]byte(strings.ToLower(comp.username)), []byte(comp.password))
115                         if err != nil {
116                                 comp.fail(err.Error())
117                                 return
118                         }
119
120                         comp.client.conn.SendCmd(&mt.ToSrvFirstSRP{
121                                 Salt:        salt,
122                                 Verifier:    verifier,
123                                 EmptyPasswd: comp.password == "",
124                         })
125                         comp.state = asVerified
126                 } else if cmd.AuthMethods == mt.SRP {
127                         var err error
128                         comp.srpBytesA, comp.bytesA, err = srp.InitiateHandshake()
129                         if err != nil {
130                                 comp.fail(err.Error())
131                                 return
132                         }
133
134                         comp.client.conn.SendCmd(&mt.ToSrvSRPBytesA{
135                                 A:      comp.srpBytesA,
136                                 NoSHA1: true,
137                         })
138                         comp.state = asRequested
139                 } else {
140                         comp.fail("invalid auth methods")
141                         return
142                 }
143
144         case *mt.ToCltSRPBytesSaltB:
145                 if !comp.checkState(asRequested, pkt) {
146                         return
147                 }
148
149                 srpBytesK, err := srp.CompleteHandshake(comp.srpBytesA, comp.bytesA, []byte(strings.ToLower(comp.username)), []byte(comp.password), cmd.Salt, cmd.B)
150                 if err != nil {
151                         comp.fail(err.Error())
152                         return
153                 }
154
155                 M := srp.ClientProof([]byte(comp.username), cmd.Salt, comp.srpBytesA, cmd.B, srpBytesK)
156                 comp.srpBytesA = []byte{}
157                 comp.bytesA = []byte{}
158
159                 if M == nil {
160                         comp.fail("srp safety check fail")
161                         return
162                 }
163
164                 comp.client.conn.SendCmd(&mt.ToSrvSRPBytesM{
165                         M: M,
166                 })
167                 comp.state = asVerified
168
169         case *mt.ToCltAcceptAuth:
170                 comp.client.conn.SendCmd(&mt.ToSrvInit2{Lang: comp.language})
171
172         case *mt.ToCltTimeOfDay:
173                 if comp.state == asActive {
174                         return
175                 }
176
177                 if !comp.checkState(asVerified, pkt) {
178                         return
179                 }
180
181                 comp.client.conn.SendCmd(&mt.ToSrvCltReady{
182                         Major:    5,
183                         Minor:    6,
184                         Patch:    0,
185                         Reserved: 0,
186                         Formspec: 4,
187                         Version:  comp.version,
188                 })
189                 comp.state = asActive
190         }
191 }
192
193 func (comp *CompAuth) accessProperty(l *lua.LState, key string, ptr *string) int {
194         if str, ok := l.Get(2).(lua.LString); ok {
195                 if comp.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_comp_auth_username(l *lua.LState) int {
207         comp := getCompAuth(l)
208         return comp.accessProperty(l, "username", &comp.username)
209 }
210
211 func l_comp_auth_password(l *lua.LState) int {
212         comp := getCompAuth(l)
213         return comp.accessProperty(l, "password", &comp.password)
214 }
215
216 func l_comp_auth_language(l *lua.LState) int {
217         comp := getCompAuth(l)
218         return comp.accessProperty(l, "language", &comp.language)
219 }
220
221 func l_comp_auth_version(l *lua.LState) int {
222         comp := getCompAuth(l)
223         return comp.accessProperty(l, "version", &comp.version)
224 }
225
226 func l_comp_auth_state(l *lua.LState) int {
227         comp := getCompAuth(l)
228
229         switch comp.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(comp.err))
241                 return 2
242         }
243
244         return 1
245 }