-
-
Notifications
You must be signed in to change notification settings - Fork 118
/
server_flow.go
273 lines (240 loc) · 6.76 KB
/
server_flow.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package exchange
import (
"context"
"math/big"
"github.com/go-faster/errors"
"go.uber.org/zap"
"github.com/gotd/td/bin"
"github.com/gotd/td/internal/crypto"
"github.com/gotd/td/internal/mt"
"github.com/gotd/td/internal/proto/codec"
)
// ServerExchangeError is returned when exchange fails due to
// some security or validation checks.
type ServerExchangeError struct {
Code int32
Err error
}
// Error implements error.
func (s *ServerExchangeError) Error() string {
return s.Err.Error()
}
// Unwrap implements error wrapper interface.
func (s *ServerExchangeError) Unwrap() error {
return s.Err
}
func serverError(code int32, err error) error {
return &ServerExchangeError{
Code: code,
Err: err,
}
}
// req_pq#60469778 or req_pq_multi#be7e8ef1
type reqPQ struct {
Type uint32
Nonce bin.Int128
}
func (r *reqPQ) Decode(b *bin.Buffer) error {
var (
legacy mt.ReqPqRequest
multi mt.ReqPqMultiRequest
)
id, err := b.PeekID()
if err != nil {
return err
}
r.Type = id
switch id {
case legacy.TypeID():
if err := legacy.Decode(b); err != nil {
return err
}
r.Nonce = legacy.Nonce
return nil
case multi.TypeID():
if err := multi.Decode(b); err != nil {
return err
}
r.Nonce = multi.Nonce
return nil
default:
return bin.NewUnexpectedID(id)
}
}
type reqOrDH struct {
Type uint32
DH mt.ReqDHParamsRequest
Req reqPQ
}
func (r *reqOrDH) Decode(b *bin.Buffer) error {
id, err := b.PeekID()
if err != nil {
return err
}
r.Type = id
switch id {
case r.DH.TypeID():
return r.DH.Decode(b)
default:
return r.Req.Decode(b)
}
}
// Run runs server-side flow.
// If b parameter is not nil, it will be used as first read message.
// Otherwise, it will be read from connection.
func (s ServerExchange) Run(ctx context.Context) (ServerExchangeResult, error) {
wrapKeyNotFound := func(err error) error {
return serverError(codec.CodeAuthKeyNotFound, err)
}
// 1. Client sends query to server
// req_pq#60469778 nonce:int128 = ResPQ; // legacy
// req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
var req reqPQ
b := new(bin.Buffer)
if err := s.readUnencrypted(ctx, b, &req); err != nil {
return ServerExchangeResult{}, err
}
s.log.Debug("Received client ReqPqMultiRequest")
serverNonce, err := crypto.RandInt128(s.rand)
if err != nil {
return ServerExchangeResult{}, errors.Wrap(err, "generate server nonce")
}
// 2. Server sends response of the form
//
// resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ;
pq, err := s.rng.PQ()
if err != nil {
return ServerExchangeResult{}, errors.Wrap(err, "generate pq")
}
SendResPQ:
s.log.Debug("Sending ResPQ", zap.String("pq", pq.String()))
if err := s.writeUnencrypted(ctx, b, &mt.ResPQ{
Pq: pq.Bytes(),
Nonce: req.Nonce,
ServerNonce: serverNonce,
ServerPublicKeyFingerprints: []int64{
s.key.Fingerprint(),
},
}); err != nil {
return ServerExchangeResult{}, err
}
// 4. Client sends query to server
//
// req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string
// q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params
var dhParams reqOrDH
if err := s.readUnencrypted(ctx, b, &dhParams); err != nil {
return ServerExchangeResult{}, err
}
switch dhParams.Type {
case mt.ReqPqRequestTypeID, mt.ReqPqMultiRequestTypeID:
// Client can send fake req_pq on start. Ignore it.
//
// Next one should be not fake.
s.log.Debug("Received ReqPQ again")
req = dhParams.Req
goto SendResPQ
default:
s.log.Debug("Received client ReqDHParamsRequest")
}
var innerData mt.PQInnerData
{
r, err := crypto.DecodeRSAPad(dhParams.DH.EncryptedData, s.key.RSA)
if err != nil {
return ServerExchangeResult{}, wrapKeyNotFound(err)
}
b.ResetTo(r)
d, err := mt.DecodePQInnerData(b)
if err != nil {
return ServerExchangeResult{}, err
}
if innerDataDC, ok := d.(*mt.PQInnerDataDC); ok && innerDataDC.DC != s.dc {
err := errors.Errorf(
"wrong DC ID, want %d, got %d",
s.dc, innerDataDC.DC,
)
return ServerExchangeResult{}, serverError(codec.CodeWrongDC, err)
}
innerData = mt.PQInnerData{
Pq: d.GetPq(),
P: d.GetP(),
Q: d.GetQ(),
Nonce: d.GetNonce(),
ServerNonce: d.GetServerNonce(),
NewNonce: d.GetNewNonce(),
}
}
dhPrime, err := s.rng.DhPrime()
if err != nil {
return ServerExchangeResult{}, errors.Wrap(err, "generate dh_prime")
}
g := 3
a, ga, err := s.rng.GA(g, dhPrime)
if err != nil {
return ServerExchangeResult{}, errors.Wrap(err, "generate g_a")
}
data := mt.ServerDHInnerData{
Nonce: req.Nonce,
ServerNonce: serverNonce,
G: g,
GA: ga.Bytes(),
DhPrime: dhPrime.Bytes(),
ServerTime: int(s.clock.Now().Unix()),
}
b.Reset()
if err := data.Encode(b); err != nil {
return ServerExchangeResult{}, err
}
key, iv := crypto.TempAESKeys(innerData.NewNonce.BigInt(), serverNonce.BigInt())
answer, err := crypto.EncryptExchangeAnswer(s.rand, b.Raw(), key, iv)
if err != nil {
return ServerExchangeResult{}, err
}
s.log.Debug("Sending ServerDHParamsOk", zap.Int("g", g))
// 5. Server responds with Server_DH_Params.
if err := s.writeUnencrypted(ctx, b, &mt.ServerDHParamsOk{
Nonce: req.Nonce,
ServerNonce: serverNonce,
EncryptedAnswer: answer,
}); err != nil {
return ServerExchangeResult{}, err
}
var clientDhParams mt.SetClientDHParamsRequest
if err := s.readUnencrypted(ctx, b, &clientDhParams); err != nil {
return ServerExchangeResult{}, err
}
s.log.Debug("Received client SetClientDHParamsRequest")
decrypted, err := crypto.DecryptExchangeAnswer(clientDhParams.EncryptedData, key, iv)
if err != nil {
err = errors.Wrap(err, "decrypt exchange answer")
return ServerExchangeResult{}, wrapKeyNotFound(err)
}
b.ResetTo(decrypted)
var clientInnerData mt.ClientDHInnerData
if err := clientInnerData.Decode(b); err != nil {
return ServerExchangeResult{}, wrapKeyNotFound(err)
}
gB := big.NewInt(0).SetBytes(clientInnerData.GB)
var authKey crypto.Key
if !crypto.FillBytes(big.NewInt(0).Exp(gB, a, dhPrime), authKey[:]) {
err := errors.New("auth_key is too big")
return ServerExchangeResult{}, wrapKeyNotFound(err)
}
// DH key exchange complete
// 8. Server responds in one of three ways:
// dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128
// new_nonce_hash1:int128 = Set_client_DH_params_answer;
s.log.Debug("Sending DhGenOk")
if err := s.writeUnencrypted(ctx, b, &mt.DhGenOk{
Nonce: req.Nonce,
ServerNonce: serverNonce,
NewNonceHash1: crypto.NonceHash1(innerData.NewNonce, authKey),
}); err != nil {
return ServerExchangeResult{}, err
}
serverSalt := crypto.ServerSalt(innerData.NewNonce, serverNonce)
return ServerExchangeResult{
Key: authKey.WithID(),
ServerSalt: serverSalt,
}, nil
}