diff --git a/internal/handshake/crypto_setup_client.go b/internal/handshake/crypto_setup_client.go index 921c005fc..2df6d6b84 100644 --- a/internal/handshake/crypto_setup_client.go +++ b/internal/handshake/crypto_setup_client.go @@ -458,7 +458,7 @@ func (h *cryptoSetupClient) addPadding(tags map[Tag][]byte) { for _, tag := range tags { size += 8 + len(tag) // 4 bytes for the tag + 4 bytes for the offset + the length of the data } - paddingSize := protocol.ClientHelloMinimumSize - size + paddingSize := protocol.MinClientHelloSize - size if paddingSize > 0 { tags[TagPAD] = bytes.Repeat([]byte{0}, paddingSize) } diff --git a/internal/handshake/crypto_setup_client_test.go b/internal/handshake/crypto_setup_client_test.go index 39bf15d92..709acb664 100644 --- a/internal/handshake/crypto_setup_client_test.go +++ b/internal/handshake/crypto_setup_client_test.go @@ -465,14 +465,14 @@ var _ = Describe("Client Crypto Setup", func() { It("is longer than the miminum client hello size", func() { err := cs.sendCHLO() Expect(err).ToNot(HaveOccurred()) - Expect(cs.cryptoStream.(*mockStream).dataWritten.Len()).To(BeNumerically(">", protocol.ClientHelloMinimumSize)) + Expect(cs.cryptoStream.(*mockStream).dataWritten.Len()).To(BeNumerically(">", protocol.MinClientHelloSize)) }) It("doesn't overflow the packet with padding", func() { tagMap := make(map[Tag][]byte) - tagMap[TagSCID] = bytes.Repeat([]byte{0}, protocol.ClientHelloMinimumSize*6/10) + tagMap[TagSCID] = bytes.Repeat([]byte{0}, protocol.MinClientHelloSize*6/10) cs.addPadding(tagMap) - Expect(len(tagMap[TagPAD])).To(BeNumerically("<", protocol.ClientHelloMinimumSize/2)) + Expect(len(tagMap[TagPAD])).To(BeNumerically("<", protocol.MinClientHelloSize/2)) }) It("saves the last sent CHLO", func() { diff --git a/internal/handshake/crypto_setup_server.go b/internal/handshake/crypto_setup_server.go index 5df110ffa..6ff11ab62 100644 --- a/internal/handshake/crypto_setup_server.go +++ b/internal/handshake/crypto_setup_server.go @@ -301,10 +301,6 @@ func (h *cryptoSetupServer) acceptSTK(token []byte) bool { } func (h *cryptoSetupServer) handleInchoateCHLO(sni string, chlo []byte, cryptoData map[Tag][]byte) ([]byte, error) { - if len(chlo) < protocol.ClientHelloMinimumSize { - return nil, qerr.Error(qerr.CryptoInvalidValueLength, "CHLO too small") - } - token, err := h.scfg.cookieGenerator.NewToken(h.remoteAddr) if err != nil { return nil, err diff --git a/internal/handshake/crypto_setup_server_test.go b/internal/handshake/crypto_setup_server_test.go index e979ceb19..99caded16 100644 --- a/internal/handshake/crypto_setup_server_test.go +++ b/internal/handshake/crypto_setup_server_test.go @@ -251,7 +251,7 @@ var _ = Describe("Server Crypto Setup", func() { It("reads the transport parameters sent by the client", func() { sourceAddrValid = true fullCHLO[TagICSL] = []byte{0x37, 0x13, 0, 0} - _, err := cs.handleMessage(bytes.Repeat([]byte{'a'}, protocol.ClientHelloMinimumSize), fullCHLO) + _, err := cs.handleMessage(bytes.Repeat([]byte{'a'}, protocol.MinClientHelloSize), fullCHLO) Expect(err).ToNot(HaveOccurred()) var params TransportParameters Expect(paramsChan).To(Receive(¶ms)) @@ -260,7 +260,7 @@ var _ = Describe("Server Crypto Setup", func() { It("generates REJ messages", func() { sourceAddrValid = false - response, err := cs.handleInchoateCHLO("", bytes.Repeat([]byte{'a'}, protocol.ClientHelloMinimumSize), nil) + response, err := cs.handleInchoateCHLO("", bytes.Repeat([]byte{'a'}, protocol.MinClientHelloSize), nil) Expect(err).ToNot(HaveOccurred()) Expect(response).To(HavePrefix("REJ")) Expect(response).To(ContainSubstring("initial public")) @@ -271,7 +271,7 @@ var _ = Describe("Server Crypto Setup", func() { It("REJ messages don't include cert or proof without STK", func() { sourceAddrValid = false - response, err := cs.handleInchoateCHLO("", bytes.Repeat([]byte{'a'}, protocol.ClientHelloMinimumSize), nil) + response, err := cs.handleInchoateCHLO("", bytes.Repeat([]byte{'a'}, protocol.MinClientHelloSize), nil) Expect(err).ToNot(HaveOccurred()) Expect(response).To(HavePrefix("REJ")) Expect(response).ToNot(ContainSubstring("certcompressed")) @@ -281,7 +281,7 @@ var _ = Describe("Server Crypto Setup", func() { It("REJ messages include cert and proof with valid STK", func() { sourceAddrValid = true - response, err := cs.handleInchoateCHLO("", bytes.Repeat([]byte{'a'}, protocol.ClientHelloMinimumSize), map[Tag][]byte{ + response, err := cs.handleInchoateCHLO("", bytes.Repeat([]byte{'a'}, protocol.MinClientHelloSize), map[Tag][]byte{ TagSTK: validSTK, TagSNI: []byte("foo"), }) @@ -337,7 +337,7 @@ var _ = Describe("Server Crypto Setup", func() { Data: map[Tag][]byte{ TagSNI: []byte("quic.clemente.io"), TagSTK: validSTK, - TagPAD: bytes.Repeat([]byte{'a'}, protocol.ClientHelloMinimumSize), + TagPAD: bytes.Repeat([]byte{'a'}, protocol.MinClientHelloSize), TagVER: versionTag, }, }.Write(&stream.dataToRead) @@ -419,11 +419,6 @@ var _ = Describe("Server Crypto Setup", func() { Expect(cs.isInchoateCHLO(fullCHLO, cert)).To(BeFalse()) }) - It("errors on too short inchoate CHLOs", func() { - _, err := cs.handleInchoateCHLO("", bytes.Repeat([]byte{'a'}, protocol.ClientHelloMinimumSize-1), nil) - Expect(err).To(MatchError("CryptoInvalidValueLength: CHLO too small")) - }) - It("rejects CHLOs without the version tag", func() { HandshakeMessage{ Tag: TagCHLO, @@ -719,7 +714,7 @@ var _ = Describe("Server Crypto Setup", func() { It("requires STK", func() { sourceAddrValid = false done, err := cs.handleMessage( - bytes.Repeat([]byte{'a'}, protocol.ClientHelloMinimumSize), + bytes.Repeat([]byte{'a'}, protocol.MinClientHelloSize), map[Tag][]byte{ TagSNI: []byte("foo"), TagVER: versionTag, @@ -733,7 +728,7 @@ var _ = Describe("Server Crypto Setup", func() { It("works with proper STK", func() { sourceAddrValid = true done, err := cs.handleMessage( - bytes.Repeat([]byte{'a'}, protocol.ClientHelloMinimumSize), + bytes.Repeat([]byte{'a'}, protocol.MinClientHelloSize), map[Tag][]byte{ TagSNI: []byte("foo"), TagVER: versionTag, diff --git a/internal/protocol/protocol.go b/internal/protocol/protocol.go index ab57153a4..4701d7d14 100644 --- a/internal/protocol/protocol.go +++ b/internal/protocol/protocol.go @@ -74,8 +74,8 @@ const MaxReceivePacketSize ByteCount = 1452 // Used in QUIC for congestion window computations in bytes. const DefaultTCPMSS ByteCount = 1460 -// ClientHelloMinimumSize is the minimum size the server expects an inchoate CHLO to have. -const ClientHelloMinimumSize = 1024 +// MinClientHelloSize is the minimum size the server expects an inchoate CHLO to have (in gQUIC) +const MinClientHelloSize = 1024 // MinInitialPacketSize is the minimum size an Initial packet (in IETF QUIC) is requried to have. const MinInitialPacketSize = 1200 diff --git a/server.go b/server.go index f2b82a3de..4ca25a269 100644 --- a/server.go +++ b/server.go @@ -328,7 +328,7 @@ func (s *server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet // since the client send a Public Header (only gQUIC has a Version Flag), we need to send a gQUIC Version Negotiation Packet if hdr.VersionFlag && !protocol.IsSupportedVersion(s.config.Versions, hdr.Version) { // drop packets that are too small to be valid first packets - if len(packet) < protocol.ClientHelloMinimumSize+len(hdr.Raw) { + if len(packet) < protocol.MinClientHelloSize+len(hdr.Raw) { return errors.New("dropping small packet with unknown version") } utils.Infof("Client offered version %s, sending VersionNegotiationPacket", hdr.Version) @@ -337,6 +337,12 @@ func (s *server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet } } + // This is (potentially) a Client Hello. + // Make sure it has the minimum required size before spending any more ressources on it. + if !sessionKnown && len(packet) < protocol.MinClientHelloSize+len(hdr.Raw) { + return errors.New("dropping small packet for unknown connection") + } + if !sessionKnown { version := hdr.Version if !protocol.IsSupportedVersion(s.config.Versions, version) { diff --git a/server_test.go b/server_test.go index 77fc88de1..7e95acc49 100644 --- a/server_test.go +++ b/server_test.go @@ -123,6 +123,7 @@ var _ = Describe("Server", func() { utils.BigEndian.WriteUint32(b, uint32(protocol.SupportedVersions[0])) firstPacket = []byte{0x09, 0x4c, 0xfa, 0x9f, 0x9b, 0x66, 0x86, 0x19, 0xf6} firstPacket = append(append(firstPacket, b.Bytes()...), 0x01) + firstPacket = append(firstPacket, bytes.Repeat([]byte{0}, protocol.MinClientHelloSize)...) // add padding }) It("returns the address", func() { @@ -340,7 +341,7 @@ var _ = Describe("Server", func() { PacketNumberLen: protocol.PacketNumberLen2, } hdr.Write(b, protocol.PerspectiveClient, 13 /* not a valid QUIC version */) - b.Write(bytes.Repeat([]byte{0}, protocol.ClientHelloMinimumSize-1)) // this packet is 1 byte too small + b.Write(bytes.Repeat([]byte{0}, protocol.MinClientHelloSize-1)) // this packet is 1 byte too small err := serv.handlePacket(conn, udpAddr, b.Bytes()) Expect(err).To(MatchError("dropping small packet with unknown version")) Expect(conn.dataWritten.Len()).Should(BeZero()) @@ -411,7 +412,7 @@ var _ = Describe("Server", func() { PacketNumberLen: protocol.PacketNumberLen2, } hdr.Write(b, protocol.PerspectiveClient, 13 /* not a valid QUIC version */) - b.Write(bytes.Repeat([]byte{0}, protocol.ClientHelloMinimumSize)) // add a fake CHLO + b.Write(bytes.Repeat([]byte{0}, protocol.MinClientHelloSize)) // add a fake CHLO conn.dataToRead <- b.Bytes() conn.dataReadFrom = udpAddr ln, err := Listen(conn, nil, config) @@ -446,7 +447,7 @@ var _ = Describe("Server", func() { } err := hdr.Write(b, protocol.PerspectiveClient, protocol.VersionTLS) Expect(err).ToNot(HaveOccurred()) - b.Write(bytes.Repeat([]byte{0}, protocol.ClientHelloMinimumSize)) // add a fake CHLO + b.Write(bytes.Repeat([]byte{0}, protocol.MinInitialPacketSize)) // add a fake CHLO conn.dataToRead <- b.Bytes() conn.dataReadFrom = udpAddr ln, err := Listen(conn, testdata.GetTLSConfig(), config) @@ -484,7 +485,7 @@ var _ = Describe("Server", func() { } err := hdr.Write(b, protocol.PerspectiveClient, protocol.VersionTLS) Expect(err).ToNot(HaveOccurred()) - b.Write(bytes.Repeat([]byte{0}, protocol.ClientHelloMinimumSize)) // add a fake CHLO + b.Write(bytes.Repeat([]byte{0}, protocol.MinClientHelloSize)) // add a fake CHLO conn.dataToRead <- b.Bytes() conn.dataReadFrom = udpAddr ln, err := Listen(conn, testdata.GetTLSConfig(), config)