1
2
3
4
5
6
7 package quic
8
9 import (
10 "bytes"
11 "context"
12 "crypto/tls"
13 "net/netip"
14 "testing"
15 "time"
16 )
17
18 type retryServerTest struct {
19 te *testEndpoint
20 originalSrcConnID []byte
21 originalDstConnID []byte
22 retry retryPacket
23 initialCrypto []byte
24 }
25
26
27
28
29 func newRetryServerTest(t *testing.T) *retryServerTest {
30 t.Helper()
31 config := &Config{
32 TLSConfig: newTestTLSConfig(serverSide),
33 RequireAddressValidation: true,
34 }
35 te := newTestEndpoint(t, config)
36 srcID := testPeerConnID(0)
37 dstID := testLocalConnID(-1)
38 params := defaultTransportParameters()
39 params.initialSrcConnID = srcID
40 initialCrypto := initialClientCrypto(t, te, params)
41
42
43
44 te.writeDatagram(&testDatagram{
45 packets: []*testPacket{{
46 ptype: packetTypeInitial,
47 num: 0,
48 version: quicVersion1,
49 srcConnID: srcID,
50 dstConnID: dstID,
51 frames: []debugFrame{
52 debugFrameCrypto{
53 data: initialCrypto,
54 },
55 },
56 }},
57 paddedSize: 1200,
58 })
59 got := te.readDatagram()
60 if len(got.packets) != 1 || got.packets[0].ptype != packetTypeRetry {
61 t.Fatalf("got datagram: %v\nwant Retry", got)
62 }
63 p := got.packets[0]
64 if got, want := p.dstConnID, srcID; !bytes.Equal(got, want) {
65 t.Fatalf("Retry destination = {%x}, want {%x}", got, want)
66 }
67
68 return &retryServerTest{
69 te: te,
70 originalSrcConnID: srcID,
71 originalDstConnID: dstID,
72 retry: retryPacket{
73 dstConnID: p.dstConnID,
74 srcConnID: p.srcConnID,
75 token: p.token,
76 },
77 initialCrypto: initialCrypto,
78 }
79 }
80
81 func TestRetryServerSucceeds(t *testing.T) {
82 rt := newRetryServerTest(t)
83 te := rt.te
84 te.advance(retryTokenValidityPeriod)
85 te.writeDatagram(&testDatagram{
86 packets: []*testPacket{{
87 ptype: packetTypeInitial,
88 num: 1,
89 version: quicVersion1,
90 srcConnID: rt.originalSrcConnID,
91 dstConnID: rt.retry.srcConnID,
92 token: rt.retry.token,
93 frames: []debugFrame{
94 debugFrameCrypto{
95 data: rt.initialCrypto,
96 },
97 },
98 }},
99 paddedSize: 1200,
100 })
101 tc := te.accept()
102 initial := tc.readPacket()
103 if initial == nil || initial.ptype != packetTypeInitial {
104 t.Fatalf("got packet:\n%v\nwant: Initial", initial)
105 }
106 handshake := tc.readPacket()
107 if handshake == nil || handshake.ptype != packetTypeHandshake {
108 t.Fatalf("got packet:\n%v\nwant: Handshake", initial)
109 }
110 if got, want := tc.sentTransportParameters.retrySrcConnID, rt.retry.srcConnID; !bytes.Equal(got, want) {
111 t.Errorf("retry_source_connection_id = {%x}, want {%x}", got, want)
112 }
113 if got, want := tc.sentTransportParameters.initialSrcConnID, initial.srcConnID; !bytes.Equal(got, want) {
114 t.Errorf("initial_source_connection_id = {%x}, want {%x}", got, want)
115 }
116 if got, want := tc.sentTransportParameters.originalDstConnID, rt.originalDstConnID; !bytes.Equal(got, want) {
117 t.Errorf("original_destination_connection_id = {%x}, want {%x}", got, want)
118 }
119 }
120
121 func TestRetryServerTokenInvalid(t *testing.T) {
122
123
124
125
126 rt := newRetryServerTest(t)
127 te := rt.te
128 te.writeDatagram(&testDatagram{
129 packets: []*testPacket{{
130 ptype: packetTypeInitial,
131 num: 1,
132 version: quicVersion1,
133 srcConnID: rt.originalSrcConnID,
134 dstConnID: rt.retry.srcConnID,
135 token: append(rt.retry.token, 0),
136 frames: []debugFrame{
137 debugFrameCrypto{
138 data: rt.initialCrypto,
139 },
140 },
141 }},
142 paddedSize: 1200,
143 })
144 te.wantDatagram("server closes connection after Initial with invalid Retry token",
145 initialConnectionCloseDatagram(
146 rt.retry.srcConnID,
147 rt.originalSrcConnID,
148 errInvalidToken))
149 }
150
151 func TestRetryServerTokenTooOld(t *testing.T) {
152
153
154 rt := newRetryServerTest(t)
155 te := rt.te
156 te.advance(retryTokenValidityPeriod + time.Second)
157 te.writeDatagram(&testDatagram{
158 packets: []*testPacket{{
159 ptype: packetTypeInitial,
160 num: 1,
161 version: quicVersion1,
162 srcConnID: rt.originalSrcConnID,
163 dstConnID: rt.retry.srcConnID,
164 token: rt.retry.token,
165 frames: []debugFrame{
166 debugFrameCrypto{
167 data: rt.initialCrypto,
168 },
169 },
170 }},
171 paddedSize: 1200,
172 })
173 te.wantDatagram("server closes connection after Initial with expired token",
174 initialConnectionCloseDatagram(
175 rt.retry.srcConnID,
176 rt.originalSrcConnID,
177 errInvalidToken))
178 }
179
180 func TestRetryServerTokenWrongIP(t *testing.T) {
181
182
183
184 rt := newRetryServerTest(t)
185 te := rt.te
186 te.writeDatagram(&testDatagram{
187 packets: []*testPacket{{
188 ptype: packetTypeInitial,
189 num: 1,
190 version: quicVersion1,
191 srcConnID: rt.originalSrcConnID,
192 dstConnID: rt.retry.srcConnID,
193 token: rt.retry.token,
194 frames: []debugFrame{
195 debugFrameCrypto{
196 data: rt.initialCrypto,
197 },
198 },
199 }},
200 paddedSize: 1200,
201 addr: netip.MustParseAddrPort("10.0.0.2:8000"),
202 })
203 te.wantDatagram("server closes connection after Initial from wrong address",
204 initialConnectionCloseDatagram(
205 rt.retry.srcConnID,
206 rt.originalSrcConnID,
207 errInvalidToken))
208 }
209
210 func TestRetryServerIgnoresRetry(t *testing.T) {
211 tc := newTestConn(t, serverSide)
212 tc.handshake()
213 tc.write(&testDatagram{
214 packets: []*testPacket{{
215 ptype: packetTypeRetry,
216 originalDstConnID: testLocalConnID(-1),
217 srcConnID: testPeerConnID(0),
218 dstConnID: testLocalConnID(0),
219 token: []byte{1, 2, 3, 4},
220 }},
221 })
222
223 tc.writeFrames(packetType1RTT, debugFramePing{})
224 tc.writeFrames(packetType1RTT, debugFramePing{})
225 tc.wantFrameType("server connection ignores spurious Retry packet",
226 packetType1RTT, debugFrameAck{})
227 }
228
229 func TestRetryClientSuccess(t *testing.T) {
230
231
232
233 tc := newTestConn(t, clientSide)
234 tc.wantFrame("client Initial CRYPTO data",
235 packetTypeInitial, debugFrameCrypto{
236 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
237 })
238 newServerConnID := []byte("new_conn_id")
239 token := []byte("token")
240 tc.write(&testDatagram{
241 packets: []*testPacket{{
242 ptype: packetTypeRetry,
243 originalDstConnID: testLocalConnID(-1),
244 srcConnID: newServerConnID,
245 dstConnID: testLocalConnID(0),
246 token: token,
247 }},
248 })
249 tc.wantPacket("client sends a new Initial packet with a token",
250 &testPacket{
251 ptype: packetTypeInitial,
252 num: 1,
253 version: quicVersion1,
254 srcConnID: testLocalConnID(0),
255 dstConnID: newServerConnID,
256 token: token,
257 frames: []debugFrame{
258 debugFrameCrypto{
259 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
260 },
261 },
262 },
263 )
264 tc.advanceToTimer()
265 tc.wantPacket("after PTO client sends another Initial packet with a token",
266 &testPacket{
267 ptype: packetTypeInitial,
268 num: 2,
269 version: quicVersion1,
270 srcConnID: testLocalConnID(0),
271 dstConnID: newServerConnID,
272 token: token,
273 frames: []debugFrame{
274 debugFrameCrypto{
275 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
276 },
277 },
278 },
279 )
280 }
281
282 func TestRetryClientInvalidServerTransportParameters(t *testing.T) {
283
284
285
286 initialSrcConnID := testPeerConnID(0)
287 originalDstConnID := testLocalConnID(-1)
288 retrySrcConnID := testPeerConnID(100)
289 for _, test := range []struct {
290 name string
291 f func(*transportParameters)
292 ok bool
293 }{{
294 name: "valid",
295 f: func(p *transportParameters) {},
296 ok: true,
297 }, {
298 name: "missing initial_source_connection_id",
299 f: func(p *transportParameters) {
300 p.initialSrcConnID = nil
301 },
302 }, {
303 name: "invalid initial_source_connection_id",
304 f: func(p *transportParameters) {
305 p.initialSrcConnID = []byte("invalid")
306 },
307 }, {
308 name: "missing original_destination_connection_id",
309 f: func(p *transportParameters) {
310 p.originalDstConnID = nil
311 },
312 }, {
313 name: "invalid original_destination_connection_id",
314 f: func(p *transportParameters) {
315 p.originalDstConnID = []byte("invalid")
316 },
317 }, {
318 name: "missing retry_source_connection_id",
319 f: func(p *transportParameters) {
320 p.retrySrcConnID = nil
321 },
322 }, {
323 name: "invalid retry_source_connection_id",
324 f: func(p *transportParameters) {
325 p.retrySrcConnID = []byte("invalid")
326 },
327 }} {
328 t.Run(test.name, func(t *testing.T) {
329 tc := newTestConn(t, clientSide,
330 func(p *transportParameters) {
331 p.initialSrcConnID = initialSrcConnID
332 p.originalDstConnID = originalDstConnID
333 p.retrySrcConnID = retrySrcConnID
334 },
335 test.f)
336 tc.ignoreFrame(frameTypeAck)
337 tc.wantFrameType("client Initial CRYPTO data",
338 packetTypeInitial, debugFrameCrypto{})
339 tc.write(&testDatagram{
340 packets: []*testPacket{{
341 ptype: packetTypeRetry,
342 originalDstConnID: originalDstConnID,
343 srcConnID: retrySrcConnID,
344 dstConnID: testLocalConnID(0),
345 token: []byte{1, 2, 3, 4},
346 }},
347 })
348 tc.wantFrameType("client resends Initial CRYPTO data",
349 packetTypeInitial, debugFrameCrypto{})
350 tc.writeFrames(packetTypeInitial,
351 debugFrameCrypto{
352 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
353 })
354 tc.writeFrames(packetTypeHandshake,
355 debugFrameCrypto{
356 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
357 })
358 if test.ok {
359 tc.wantFrameType("valid params, client sends Handshake",
360 packetTypeHandshake, debugFrameCrypto{})
361 } else {
362 tc.wantFrame("invalid transport parameters",
363 packetTypeInitial, debugFrameConnectionCloseTransport{
364 code: errTransportParameter,
365 })
366 }
367 })
368 }
369 }
370
371 func TestRetryClientIgnoresRetryAfterReceivingPacket(t *testing.T) {
372
373
374
375 tc := newTestConn(t, clientSide)
376 tc.ignoreFrame(frameTypeAck)
377 tc.ignoreFrame(frameTypeNewConnectionID)
378 tc.wantFrameType("client Initial CRYPTO data",
379 packetTypeInitial, debugFrameCrypto{})
380 tc.writeFrames(packetTypeInitial,
381 debugFrameCrypto{
382 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
383 })
384 retry := &testDatagram{
385 packets: []*testPacket{{
386 ptype: packetTypeRetry,
387 originalDstConnID: testLocalConnID(-1),
388 srcConnID: testPeerConnID(100),
389 dstConnID: testLocalConnID(0),
390 token: []byte{1, 2, 3, 4},
391 }},
392 }
393 tc.write(retry)
394 tc.wantIdle("client ignores Retry after receiving Initial packet")
395 tc.writeFrames(packetTypeHandshake,
396 debugFrameCrypto{
397 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
398 })
399 tc.wantFrameType("client Handshake CRYPTO data",
400 packetTypeHandshake, debugFrameCrypto{})
401 tc.write(retry)
402 tc.wantIdle("client ignores Retry after discarding Initial keys")
403 }
404
405 func TestRetryClientIgnoresRetryAfterReceivingRetry(t *testing.T) {
406
407
408
409 tc := newTestConn(t, clientSide)
410 tc.wantFrameType("client Initial CRYPTO data",
411 packetTypeInitial, debugFrameCrypto{})
412 retry := &testDatagram{
413 packets: []*testPacket{{
414 ptype: packetTypeRetry,
415 originalDstConnID: testLocalConnID(-1),
416 srcConnID: testPeerConnID(100),
417 dstConnID: testLocalConnID(0),
418 token: []byte{1, 2, 3, 4},
419 }},
420 }
421 tc.write(retry)
422 tc.wantFrameType("client resends Initial CRYPTO data",
423 packetTypeInitial, debugFrameCrypto{})
424 tc.write(retry)
425 tc.wantIdle("client ignores second Retry")
426 }
427
428 func TestRetryClientIgnoresRetryWithInvalidIntegrityTag(t *testing.T) {
429 tc := newTestConn(t, clientSide)
430 tc.wantFrameType("client Initial CRYPTO data",
431 packetTypeInitial, debugFrameCrypto{})
432 pkt := encodeRetryPacket(testLocalConnID(-1), retryPacket{
433 srcConnID: testPeerConnID(100),
434 dstConnID: testLocalConnID(0),
435 token: []byte{1, 2, 3, 4},
436 })
437 pkt[len(pkt)-1] ^= 1
438 tc.endpoint.write(&datagram{
439 b: pkt,
440 addr: testClientAddr,
441 })
442 tc.wantIdle("client ignores Retry with invalid integrity tag")
443 }
444
445 func TestRetryClientIgnoresRetryWithZeroLengthToken(t *testing.T) {
446
447
448 tc := newTestConn(t, clientSide)
449 tc.wantFrameType("client Initial CRYPTO data",
450 packetTypeInitial, debugFrameCrypto{})
451 tc.write(&testDatagram{
452 packets: []*testPacket{{
453 ptype: packetTypeRetry,
454 originalDstConnID: testLocalConnID(-1),
455 srcConnID: testPeerConnID(100),
456 dstConnID: testLocalConnID(0),
457 token: []byte{},
458 }},
459 })
460 tc.wantIdle("client ignores Retry with zero-length token")
461 }
462
463 func TestRetryStateValidateInvalidToken(t *testing.T) {
464
465
466 var rs retryState
467 if err := rs.init(); err != nil {
468 t.Fatal(err)
469 }
470 nonce := make([]byte, rs.aead.NonceSize())
471 now := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
472 srcConnID := []byte{1, 2, 3, 4}
473 dstConnID := nonce[:20]
474 addr := testClientAddr
475
476 for _, test := range []struct {
477 name string
478 token []byte
479 }{{
480 name: "token too short",
481 token: []byte{1, 2, 3},
482 }, {
483 name: "token plaintext too short",
484 token: func() []byte {
485 plaintext := make([]byte, 7)
486 token := append([]byte{}, nonce[20:]...)
487 return rs.aead.Seal(token, nonce, plaintext, rs.additionalData(srcConnID, addr))
488 }(),
489 }} {
490 t.Run(test.name, func(t *testing.T) {
491 if _, ok := rs.validateToken(now, test.token, srcConnID, dstConnID, addr); ok {
492 t.Errorf("validateToken succeeded, want failure")
493 }
494 })
495 }
496 }
497
498 func TestParseInvalidRetryPackets(t *testing.T) {
499 originalDstConnID := []byte{1, 2, 3, 4}
500 goodPkt := encodeRetryPacket(originalDstConnID, retryPacket{
501 dstConnID: []byte{1},
502 srcConnID: []byte{2},
503 token: []byte{3},
504 })
505 for _, test := range []struct {
506 name string
507 pkt []byte
508 }{{
509 name: "packet too short",
510 pkt: goodPkt[:len(goodPkt)-4],
511 }, {
512 name: "packet header invalid",
513 pkt: goodPkt[:5],
514 }, {
515 name: "integrity tag invalid",
516 pkt: func() []byte {
517 pkt := cloneBytes(goodPkt)
518 pkt[len(pkt)-1] ^= 1
519 return pkt
520 }(),
521 }} {
522 t.Run(test.name, func(t *testing.T) {
523 if _, ok := parseRetryPacket(test.pkt, originalDstConnID); ok {
524 t.Errorf("parseRetryPacket succeded, want failure")
525 }
526 })
527 }
528 }
529
530 func initialClientCrypto(t *testing.T, e *testEndpoint, p transportParameters) []byte {
531 t.Helper()
532 config := &tls.QUICConfig{TLSConfig: newTestTLSConfig(clientSide)}
533 tlsClient := tls.QUICClient(config)
534 tlsClient.SetTransportParameters(marshalTransportParameters(p))
535 tlsClient.Start(context.Background())
536 t.Cleanup(func() {
537 tlsClient.Close()
538 })
539 e.peerTLSConn = tlsClient
540 var data []byte
541 for {
542 e := tlsClient.NextEvent()
543 switch e.Kind {
544 case tls.QUICNoEvent:
545 return data
546 case tls.QUICWriteData:
547 if e.Level != tls.QUICEncryptionLevelInitial {
548 t.Fatal("initial data at unexpected level")
549 }
550 data = append(data, e.Data...)
551 }
552 }
553 }
554
555 func initialConnectionCloseDatagram(srcConnID, dstConnID []byte, code transportError) *testDatagram {
556 return &testDatagram{
557 packets: []*testPacket{{
558 ptype: packetTypeInitial,
559 num: 0,
560 version: quicVersion1,
561 srcConnID: srcConnID,
562 dstConnID: dstConnID,
563 frames: []debugFrame{
564 debugFrameConnectionCloseTransport{
565 code: code,
566 },
567 },
568 }},
569 }
570 }
571
View as plain text