golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/stateless_reset_test.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build go1.21 6 7 package quic 8 9 import ( 10 "bytes" 11 "context" 12 "crypto/rand" 13 "crypto/tls" 14 "errors" 15 "net/netip" 16 "testing" 17 "time" 18 ) 19 20 func TestStatelessResetClientSendsStatelessResetTokenTransportParameter(t *testing.T) { 21 // "[The stateless_reset_token] transport parameter MUST NOT be sent by a client [...]" 22 // https://www.rfc-editor.org/rfc/rfc9000#section-18.2-4.6.1 23 resetToken := testPeerStatelessResetToken(0) 24 tc := newTestConn(t, serverSide, func(p *transportParameters) { 25 p.statelessResetToken = resetToken[:] 26 }) 27 tc.writeFrames(packetTypeInitial, 28 debugFrameCrypto{ 29 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial], 30 }) 31 tc.wantFrame("client provided stateless_reset_token transport parameter", 32 packetTypeInitial, debugFrameConnectionCloseTransport{ 33 code: errTransportParameter, 34 }) 35 } 36 37 var testStatelessResetKey = func() (key [32]byte) { 38 if _, err := rand.Read(key[:]); err != nil { 39 panic(err) 40 } 41 return key 42 }() 43 44 func testStatelessResetToken(cid []byte) statelessResetToken { 45 var gen statelessResetTokenGenerator 46 gen.init(testStatelessResetKey) 47 return gen.tokenForConnID(cid) 48 } 49 50 func testLocalStatelessResetToken(seq int64) statelessResetToken { 51 return testStatelessResetToken(testLocalConnID(seq)) 52 } 53 54 func newDatagramForReset(cid []byte, size int, addr netip.AddrPort) *datagram { 55 dgram := append([]byte{headerFormShort | fixedBit}, cid...) 56 for len(dgram) < size { 57 dgram = append(dgram, byte(len(dgram))) // semi-random junk 58 } 59 return &datagram{ 60 b: dgram, 61 peerAddr: addr, 62 } 63 } 64 65 func TestStatelessResetSentSizes(t *testing.T) { 66 config := &Config{ 67 TLSConfig: newTestTLSConfig(serverSide), 68 StatelessResetKey: testStatelessResetKey, 69 } 70 addr := netip.MustParseAddr("127.0.0.1") 71 te := newTestEndpoint(t, config) 72 for i, test := range []struct { 73 reqSize int 74 wantSize int 75 }{{ 76 // Datagrams larger than 42 bytes result in a 42-byte stateless reset. 77 // This isn't specifically mandated by RFC 9000, but is implied. 78 // https://www.rfc-editor.org/rfc/rfc9000#section-10.3-11 79 reqSize: 1200, 80 wantSize: 42, 81 }, { 82 // "An endpoint that sends a Stateless Reset in response to a packet 83 // that is 43 bytes or shorter SHOULD send a Stateless Reset that is 84 // one byte shorter than the packet it responds to." 85 // https://www.rfc-editor.org/rfc/rfc9000#section-10.3-11 86 reqSize: 43, 87 wantSize: 42, 88 }, { 89 reqSize: 42, 90 wantSize: 41, 91 }, { 92 // We should send a stateless reset in response to the smallest possible 93 // valid datagram the peer can send us. 94 // The smallest packet is 1-RTT: 95 // header byte, conn id, packet num, payload, AEAD. 96 reqSize: 1 + connIDLen + 1 + 1 + 16, 97 wantSize: 1 + connIDLen + 1 + 1 + 16 - 1, 98 }, { 99 // The smallest possible stateless reset datagram is 21 bytes. 100 // Since our response must be smaller than the incoming datagram, 101 // we must not respond to a 21 byte or smaller packet. 102 reqSize: 21, 103 wantSize: 0, 104 }} { 105 cid := testLocalConnID(int64(i)) 106 token := testStatelessResetToken(cid) 107 addrport := netip.AddrPortFrom(addr, uint16(8000+i)) 108 te.write(newDatagramForReset(cid, test.reqSize, addrport)) 109 110 got := te.read() 111 if len(got) != test.wantSize { 112 t.Errorf("got %v-byte response to %v-byte req, want %v", 113 len(got), test.reqSize, test.wantSize) 114 } 115 if len(got) == 0 { 116 continue 117 } 118 // "Endpoints MUST send Stateless Resets formatted as 119 // a packet with a short header." 120 // https://www.rfc-editor.org/rfc/rfc9000#section-10.3-15 121 if isLongHeader(got[0]) { 122 t.Errorf("response to %v-byte request is not a short-header packet\ngot: %x", test.reqSize, got) 123 } 124 if !bytes.HasSuffix(got, token[:]) { 125 t.Errorf("response to %v-byte request does not end in stateless reset token\ngot: %x\nwant suffix: %x", test.reqSize, got, token) 126 } 127 } 128 } 129 130 func TestStatelessResetSuccessfulNewConnectionID(t *testing.T) { 131 // "[...] Stateless Reset Token field values from [...] NEW_CONNECTION_ID frames [...]" 132 // https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1-1 133 qr := &qlogRecord{} 134 tc := newTestConn(t, clientSide, qr.config) 135 tc.handshake() 136 tc.ignoreFrame(frameTypeAck) 137 138 // Retire connection ID 0. 139 tc.writeFrames(packetType1RTT, 140 debugFrameNewConnectionID{ 141 retirePriorTo: 1, 142 seq: 2, 143 connID: testPeerConnID(2), 144 }) 145 tc.wantFrame("peer requested we retire conn id 0", 146 packetType1RTT, debugFrameRetireConnectionID{ 147 seq: 0, 148 }) 149 150 resetToken := testPeerStatelessResetToken(1) // provided during handshake 151 dgram := append(make([]byte, 100), resetToken[:]...) 152 tc.endpoint.write(&datagram{ 153 b: dgram, 154 }) 155 156 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, errStatelessReset) { 157 t.Errorf("conn.Wait() = %v, want errStatelessReset", err) 158 } 159 tc.wantIdle("closed connection is idle in draining") 160 tc.advance(1 * time.Second) // long enough to exit the draining state 161 tc.wantIdle("closed connection is idle after draining") 162 163 qr.wantEvents(t, jsonEvent{ 164 "name": "connectivity:connection_closed", 165 "data": map[string]any{ 166 "trigger": "stateless_reset", 167 }, 168 }) 169 } 170 171 func TestStatelessResetSuccessfulTransportParameter(t *testing.T) { 172 // "[...] Stateless Reset Token field values from [...] 173 // the server's transport parameters [...]" 174 // https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1-1 175 resetToken := testPeerStatelessResetToken(0) 176 tc := newTestConn(t, clientSide, func(p *transportParameters) { 177 p.statelessResetToken = resetToken[:] 178 }) 179 tc.handshake() 180 181 dgram := append(make([]byte, 100), resetToken[:]...) 182 tc.endpoint.write(&datagram{ 183 b: dgram, 184 }) 185 186 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, errStatelessReset) { 187 t.Errorf("conn.Wait() = %v, want errStatelessReset", err) 188 } 189 tc.wantIdle("closed connection is idle") 190 } 191 192 func TestStatelessResetSuccessfulPrefix(t *testing.T) { 193 for _, test := range []struct { 194 name string 195 prefix []byte 196 size int 197 }{{ 198 name: "short header and fixed bit", 199 prefix: []byte{ 200 headerFormShort | fixedBit, 201 }, 202 size: 100, 203 }, { 204 // "[...] endpoints MUST treat [long header packets] ending in a 205 // valid stateless reset token as a Stateless Reset [...]" 206 // https://www.rfc-editor.org/rfc/rfc9000#section-10.3-15 207 name: "long header no fixed bit", 208 prefix: []byte{ 209 headerFormLong, 210 }, 211 size: 100, 212 }, { 213 // "[...] the comparison MUST be performed when the first packet 214 // in an incoming datagram [...] cannot be decrypted." 215 // https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1-2 216 name: "short header valid DCID", 217 prefix: append([]byte{ 218 headerFormShort | fixedBit, 219 }, testLocalConnID(0)...), 220 size: 100, 221 }, { 222 name: "handshake valid DCID", 223 prefix: append([]byte{ 224 headerFormLong | fixedBit | longPacketTypeHandshake, 225 }, testLocalConnID(0)...), 226 size: 100, 227 }, { 228 name: "no fixed bit valid DCID", 229 prefix: append([]byte{ 230 0, 231 }, testLocalConnID(0)...), 232 size: 100, 233 }} { 234 t.Run(test.name, func(t *testing.T) { 235 resetToken := testPeerStatelessResetToken(0) 236 tc := newTestConn(t, clientSide, func(p *transportParameters) { 237 p.statelessResetToken = resetToken[:] 238 }) 239 tc.handshake() 240 241 dgram := test.prefix 242 for len(dgram) < test.size-len(resetToken) { 243 dgram = append(dgram, byte(len(dgram))) // semi-random junk 244 } 245 dgram = append(dgram, resetToken[:]...) 246 tc.endpoint.write(&datagram{ 247 b: dgram, 248 }) 249 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, errStatelessReset) { 250 t.Errorf("conn.Wait() = %v, want errStatelessReset", err) 251 } 252 }) 253 } 254 } 255 256 func TestStatelessResetRetiredConnID(t *testing.T) { 257 // "An endpoint MUST NOT check for any stateless reset tokens [...] 258 // for connection IDs that have been retired." 259 // https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1-3 260 resetToken := testPeerStatelessResetToken(0) 261 tc := newTestConn(t, clientSide, func(p *transportParameters) { 262 p.statelessResetToken = resetToken[:] 263 }) 264 tc.handshake() 265 tc.ignoreFrame(frameTypeAck) 266 267 // We retire connection ID 0. 268 tc.writeFrames(packetType1RTT, 269 debugFrameNewConnectionID{ 270 seq: 2, 271 retirePriorTo: 1, 272 connID: testPeerConnID(2), 273 }) 274 tc.wantFrame("peer asked for conn id 0 to be retired", 275 packetType1RTT, debugFrameRetireConnectionID{ 276 seq: 0, 277 }) 278 279 // Receive a stateless reset for connection ID 0. 280 dgram := append(make([]byte, 100), resetToken[:]...) 281 tc.endpoint.write(&datagram{ 282 b: dgram, 283 }) 284 285 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, context.Canceled) { 286 t.Errorf("conn.Wait() = %v, want connection to be alive", err) 287 } 288 }